programing

여러 열에서 최소값을 선택하는 가장 좋은 방법은 무엇입니까?

codeshow 2023. 4. 17. 22:10
반응형

여러 열에서 최소값을 선택하는 가장 좋은 방법은 무엇입니까?

SQL Server 2005에서 다음 표를 참조하십시오.

ID   Col1   Col2   Col3
--   ----   ----   ----
1       3     34     76  
2      32    976     24
3       7    235      3
4     245      1    792

다음 결과를 생성하는 쿼리를 작성하는 가장 좋은 방법은 무엇입니까(즉, 최종 컬럼을 생성하는 컬럼 - 각 의 Col1, Col2, Col3 중 최소값이 포함된 컬럼)?

ID   Col1   Col2   Col3  TheMin
--   ----   ----   ----  ------
1       3     34     76       3
2      32    976     24      24
3       7    235      3       3
4     245      1    792       1

갱신:

실제 시나리오에서는 설명했듯이 데이터베이스가 적절하게 정규화되어 있습니다.이러한 "배열" 열은 실제 테이블에는 없지만 보고서에 필요한 결과 집합에 있습니다.또한 보고서에 MinValue 열도 필요합니다.기본 결과 세트를 변경할 수 없기 때문에 편리한 "탈옥 카드"를 위해 T-SQL을 찾고 있었습니다.

아래의 CASE 어프로치를 사용해 보았습니다만, 조금 번거롭지만 효과가 있습니다.또한 같은 행에 두 개의 최소값이 있다는 사실을 고려해야 하기 때문에 답변보다 더 복잡합니다.

어쨌든, 현재의 솔루션을 투고하고 싶다고 생각했습니다만, 제 제약에 의해 꽤 효과가 있습니다.UNPIVOT 연산자를 사용합니다.

with cte (ID, Col1, Col2, Col3)
as
(
    select ID, Col1, Col2, Col3
    from TestTable
)
select cte.ID, Col1, Col2, Col3, TheMin from cte
join
(
    select
        ID, min(Amount) as TheMin
    from 
        cte 
        UNPIVOT (Amount for AmountCol in (Col1, Col2, Col3)) as unpvt
    group by ID
) as minValues
on cte.ID = minValues.ID

이것이 최고의 성능을 제공할 것으로 기대하지는 않지만, 상황을 고려할 때(새로운 MinValue 컬럼 요건만으로 모든 쿼리를 재설계할 수는 없습니다) 매우 우아한 "탈옥 카드"입니다.

이것을 달성하기 위한 많은 방법이 있을 것이다.케이스/시기를 사용하는 것이 좋습니다.3열이면 나쁘지 않아요.

Select Id,
       Case When Col1 < Col2 And Col1 < Col3 Then Col1
            When Col2 < Col1 And Col2 < Col3 Then Col2 
            Else Col3
            End As TheMin
From   YourTableNameHere

「」를 사용합니다.CROSS APPLY:

SELECT ID, Col1, Col2, Col3, MinValue
FROM YourTable
CROSS APPLY (SELECT MIN(d) AS MinValue FROM (VALUES (Col1), (Col2), (Col3)) AS a(d)) A

SQL 바이올린

SELECT ID, Col1, Col2, Col3, 
    (SELECT MIN(Col) FROM (VALUES (Col1), (Col2), (Col3)) AS X(Col)) AS TheMin
FROM Table

MySQL에서는 다음을 사용합니다.

select least(col1, col2, col3) FROM yourtable

"brute force" 접근방식을 트위스트와 함께 사용할 수 있습니다.

SELECT CASE
    WHEN Col1 <= Col2 AND Col1 <= Col3 THEN Col1
    WHEN                  Col2 <= Col3 THEN Col2
    ELSE                                    Col3
END AS [Min Value] FROM [Your Table]

첫 번째 조건이 실패했을 때 Col1이 최소값이 아니라는 것을 보증하기 때문에 나머지 조건에서 Col1을 제거할 수 있습니다.후속 조건도 마찬가지입니다.5개의 열에 대해 쿼리는 다음과 같습니다.

SELECT CASE
    WHEN Col1 <= Col2 AND Col1 <= Col3 AND Col1 <= Col4 AND Col1 <= Col5 THEN Col1
    WHEN                  Col2 <= Col3 AND Col2 <= Col4 AND Col2 <= Col5 THEN Col2
    WHEN                                   Col3 <= Col4 AND Col3 <= Col5 THEN Col3
    WHEN                                                    Col4 <= Col5 THEN Col4
    ELSE                                                                      Col5
END AS [Min Value] FROM [Your Table]

개 , " "는 " " " 입니다.<=이 시스템을 종료할 수 있습니다.CASE★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

예시와 같이 열이 정수인 경우 함수를 만듭니다.

create function f_min_int(@a as int, @b as int) 
returns int
as
begin
    return case when @a < @b then @a else coalesce(@b,@a) end
end

사용할 필요가 있을 때는, 다음과 같이 합니다.

select col1, col2, col3, dbo.f_min_int(dbo.f_min_int(col1,col2),col3)

만약 당신이 5컬럼을 가지고 있다면, 위는

select col1, col2, col3, col4, col5,
dbo.f_min_int(dbo.f_min_int(dbo.f_min_int(dbo.f_min_int(col1,col2),col3),col4),col5)

가장 좋은 방법은 아마도 그렇게 하지 않는 입니다.사람들은 의미 있는 정보를 추출하기 위해 SQL "체조"를 필요로 하는 방식으로 데이터를 저장하기를 고집하는데, 스키마를 조금만 더 잘 구성하면 원하는 결과를 얻을 수 있는 훨씬 쉬운 방법이 있습니다.-)

올바른 방법은 다음과 같은 표를 작성하는 것이라고 생각합니다.

ID    Col    Val
--    ---    ---
 1      1      3
 1      2     34
 1      3     76

 2      1     32
 2      2    976
 2      3     24

 3      1      7
 3      2    235
 3      3      3

 4      1    245
 4      2      1
 4      3    792

ID/Col 키한 경우)로서Col을 이용하다.select min(val) from tbl ' 기둥은 '오래된 기둥'을 다룰 수 있습니다.where col = 2를 참조해 주세요.이를 통해 '오래된 열'의 수가 증가하더라도 쉽게 확장할 수 있습니다.

이렇게 하면 문의가 훨씬 쉬워집니다.일반적으로 사용하는 가이드라인은 데이터베이스 행에 배열과 같은 것이 있는 경우 잘못된 작업을 하고 있을 수 있으므로 데이터 재구성을 고려해야 한다는 것입니다.


그러나 어떤 이유로 이러한 열을 변경할 수 없는 경우 삽입 및 업데이트 트리거를 사용하고 이러한 트리거가 최소값으로 설정된 다른 열을 추가하는 것이 좋습니다.Col1/2/3이렇게 하면 작업의 '비용'이 선택 항목에서 해당 항목이 속한 업데이트/삽입으로 이동됩니다. 제 경험상 대부분의 데이터베이스 테이블은 쓰기보다 읽기 빈도가 훨씬 높기 때문에 쓰기 비용이 시간이 지남에 따라 더 효율적으로 발생하는 경향이 있습니다.

즉, 행의 최소값은 다른 열 중 하나가 변경될 때만 변경되므로 이때 계산해야 합니다. 선택할 때마다 계산하면 안 됩니다(데이터가 변경되지 않으면 낭비됩니다).그러면 다음과 같은 표가 나타납니다.

ID   Col1   Col2   Col3   MinVal
--   ----   ----   ----   ------
 1      3     34     76        3
 2     32    976     24       24
 3      7    235      3        3
 4    245      1    792        1

외, 「 」에서 을 내릴 가 있는 .select데이터는 삽입/갱신 시에만 변경되므로 일반적으로 퍼포먼스 면에서 시간은 좋지 않습니다.다른 컬럼을 추가하면 DB 내의 공간이 더 많이 차지되고 삽입 및 업데이트에는 다소 느리지만 선택에는 훨씬 더 빠를 수 있습니다.기재한 바와 같이 바람직한 접근법은 우선순위에 따라 달라집니다.대부분의 표는 쓰여진 것보다 훨씬 더 자주 읽힌다.

조합 쿼리를 사용하여 이 작업을 수행할 수도 있습니다.열 수가 증가함에 따라 쿼리를 수정해야 하지만 적어도 직접 수정해야 합니다.

Select T.Id, T.Col1, T.Col2, T.Col3, A.TheMin
From   YourTable T
       Inner Join (
         Select A.Id, Min(A.Col1) As TheMin
         From   (
                Select Id, Col1
                From   YourTable

                Union All

                Select Id, Col2
                From   YourTable

                Union All

                Select Id, Col3
                From   YourTable
                ) As A
         Group By A.Id
       ) As A
       On T.Id = A.Id

이것은 폭력이지만 효과가 있다.

 select case when col1 <= col2 and col1 <= col3 then col1
           case when col2 <= col1 and col2 <= col3 then col2
           case when col3 <= col1 and col3 <= col2 then col3
    as 'TheMin'
           end

from Table T

min()은 하나의 열에서만 동작하며 여러 열에서는 동작하지 않기 때문입니다.

질문도 질문도 이 질문도 이 질문에 답하려고 합니다.

요약하자면 Oracle에는 이를 위한 함수가 내장되어 있으며 SQL Server에서는 사용자 정의 함수를 정의하거나 사례문을 사용해야 합니다.

여러 열의 경우 CASE 문을 사용하는 것이 가장 좋지만 두 숫자 열 i 및 j의 경우 간단한 산술을 사용할 수 있습니다.

min(i,j) = (i+j)/2 - abs(i-j)/2

이 공식은 여러 열의 최소값을 얻기 위해 사용할 수 있지만 정말로 지저분한 과거 2, min(i,j,k)은 min(i,min(j,k)입니다.

저장 프로시저를 만들 수 있다면 여러 가지 값이 필요할 수 있습니다.그냥 그렇게 부를 수 있습니다.

select *,
case when column1 < columnl2 And column1 < column3 then column1
when columnl2 < column1 And columnl2 < column3 then columnl2
else column3
end As minValue
from   tbl_example

조합의 질문에 약간의 반전이 있습니다.

DECLARE @Foo TABLE (ID INT, Col1 INT, Col2 INT, Col3 INT)

INSERT @Foo (ID, Col1, Col2, Col3)
VALUES
(1, 3, 34, 76),
(2, 32, 976, 24),
(3, 7, 235, 3),
(4, 245, 1, 792)

SELECT
    ID,
    Col1,
    Col2,
    Col3,
    (
        SELECT MIN(T.Col)
        FROM
        (
            SELECT Foo.Col1 AS Col UNION ALL
            SELECT Foo.Col2 AS Col UNION ALL
            SELECT Foo.Col3 AS Col 
        ) AS T
    ) AS TheMin
FROM
    @Foo AS Foo

SQL 2005를 사용하면 다음과 같이 깔끔한 작업을 수행할 수 있습니다.

;WITH    res
          AS ( SELECT   t.YourID ,
                        CAST(( SELECT   Col1 AS c01 ,
                                        Col2 AS c02 ,
                                        Col3 AS c03 ,
                                        Col4 AS c04 ,
                                        Col5 AS c05
                               FROM     YourTable AS cols
                               WHERE    YourID = t.YourID
                             FOR
                               XML AUTO ,
                                   ELEMENTS
                             ) AS XML) AS colslist
               FROM     YourTable AS t
             )
    SELECT  YourID ,
            colslist.query('for $c in //cols return min(data($c/*))').value('.',
                                            'real') AS YourMin ,
            colslist.query('for $c in //cols return avg(data($c/*))').value('.',
                                            'real') AS YourAvg ,
            colslist.query('for $c in //cols return max(data($c/*))').value('.',
                                            'real') AS YourMax
    FROM    res

이렇게 하면 많은 연산자가 혼란스러워지지 않습니다.

다만, 이것은 다른 선택보다 느릴 수 있습니다.

네 선택이야

찾고 있는 값(통상은 상태 코드)을 알고 있는 경우는, 다음의 정보가 도움이 됩니다.

select case when 0 in (PAGE1STATUS ,PAGE2STATUS ,PAGE3STATUS,
PAGE4STATUS,PAGE5STATUS ,PAGE6STATUS) then 0 else 1 end
FROM CUSTOMERS_FORMS

아래에서는 최소 몇 개의 날짜를 얻기 위해 임시 테이블을 사용합니다.첫 번째 임시 테이블은 조인된 여러 테이블을 쿼리하여 다양한 날짜(쿼리의 다른 값도 포함)를 가져옵니다.다음 임시 테이블은 날짜 열의 수만큼 패스를 사용하여 다양한 열과 최소 날짜를 가져옵니다.

이는 본질적으로 유니언 쿼리와 같으며, 같은 수의 패스가 필요하지만 더 효율적일 수 있습니다(경험에 따라 다르지만 테스트가 필요합니다).이 경우 효율성은 문제가 되지 않았습니다(8,000장의 레코드).색인 등을 할 수 있습니다.

--==================== this gets minimums and global min
if object_id('tempdb..#temp1') is not null
    drop table #temp1
if object_id('tempdb..#temp2') is not null
    drop table #temp2

select r.recordid ,  r.ReferenceNumber, i.InventionTitle, RecordDate, i.ReceivedDate
, min(fi.uploaddate) [Min File Upload], min(fi.CorrespondenceDate) [Min File Correspondence]
into #temp1
from record r 
join Invention i on i.inventionid = r.recordid
left join LnkRecordFile lrf on lrf.recordid = r.recordid
left join fileinformation fi on fi.fileid = lrf.fileid
where r.recorddate > '2015-05-26'
 group by  r.recordid, recorddate, i.ReceivedDate,
 r.ReferenceNumber, i.InventionTitle



select recordid, recorddate [min date]
into #temp2
from #temp1

update #temp2
set [min date] = ReceivedDate 
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
where t1.ReceivedDate < [min date] and  t1.ReceivedDate > '2001-01-01'

update #temp2 
set [min date] = t1.[Min File Upload]
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
where t1.[Min File Upload] < [min date] and  t1.[Min File Upload] > '2001-01-01'

update #temp2
set [min date] = t1.[Min File Correspondence]
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
where t1.[Min File Correspondence] < [min date] and t1.[Min File Correspondence] > '2001-01-01'


select t1.*, t2.[min date] [LOWEST DATE]
from #temp1 t1 join #temp2 t2 on t1.recordid = t2.recordid
order by t1.recordid
SELECT [ID],
            (
                SELECT MIN([value].[MinValue])
                FROM
                (
                    VALUES
                        ([Col1]),
                        ([Col1]),
                        ([Col2]),
                        ([Col3])
                ) AS [value] ([MinValue])
           ) AS [MinValue]
FROM Table;

콜1 < Col1 < Col2 Col1 < Col2 Col2 색상, Col2 Col2 색상, Col2 열에 있는 경우 컬럼 2 null < Col2 null < 콜3 및 콜1은 null입니다.Col3 < Col1 and Col3 < Col2 > Col3 이면 Col3 이면 Col3 이면 Col3 이면 Col3 이면 Col3 이면 Col3 이면 Col3 이면 Col3 이면 Col3 이면 Col2 = Col2 이면 Col3 이면 Col2 입니다.
'MIN'으로 끝나며, 'MIN'은 NULL로 끝납니다.

오래된 질문인 것은 알지만, 나는 여전히 답이 필요했고 다른 답변에 만족하지 않았기 때문에 @paxdiablo의 답변에 반전을 주는 나만의 질문을 고안해야 했다.


저는 SAP ASE 16.0에서 왔고, IMHO가 한 줄의 다른 열에 유효하게 저장되어 있는 특정 데이터의 통계를 엿볼 필요가 있었습니다(IMHO는 계획된 시간, 작업 시작 시 예상된 내용, 마지막으로 실제 시간을 나타냅니다).따라서 나는 열을 임시 테이블의 행으로 바꾸고 평소처럼 이에 대한 쿼리를 작성했다.

N.B. 만능 솔루션이 아닙니다!

CREATE TABLE #tempTable (ID int, columnName varchar(20), dataValue int)

INSERT INTO #tempTable 
  SELECT ID, 'Col1', Col1
    FROM sourceTable
   WHERE Col1 IS NOT NULL
INSERT INTO #tempTable 
  SELECT ID, 'Col2', Col2
    FROM sourceTable
   WHERE Col2 IS NOT NULL
INSERT INTO #tempTable 
  SELECT ID, 'Col3', Col3
    FROM sourceTable
   WHERE Col3 IS NOT NULL

SELECT ID
     , min(dataValue) AS 'Min'
     , max(dataValue) AS 'Max'
     , max(dataValue) - min(dataValue) AS 'Diff' 
  FROM #tempTable 
  GROUP BY ID

이 작업은 63만 행의 소스 세트에서 30초 정도 걸리고 인덱스 데이터만 사용했기 때문에 시간상 중요한 프로세스에서는 실행할 수 없습니다.단, 일회성 데이터 검사나 종료 보고서 같은 경우에는 문제가 없습니다(동료 또는 상관에게 확인해 주십시오).이 스타일의 주된 장점은 데이터를 복사한 후에는 열을 더 많이/적게 사용하고 그룹화, 필터링 등을 쉽게 변경할 수 있다는 것입니다.

추가 데이터(columnName,maxes,는 내을 주기 너는 로 하지 es, ...)의 여기에 .아이디어를 내기 위해서 남겨둔 것입니다.

2022 Azure Database Server 2022(© Azure SQL Database)를 사용할 수 .에서는LEAST()★★★★★★ 。

SELECT LEAST('6.62', 3.1415, N'7') AS LeastVal;
SELECT LEAST('Glacier', N'Joshua Tree', 'Mount Rainier') AS LeastString;

SELECT 
    LEAST(P.SellStartDate, P.OtherDate, P.ThirdDate) AS EarliestDate
FROM SalesLT.Product AS P

https://learn.microsoft.com/en-us/sql/t-sql/functions/logical-functions-least-transact-sql?view=azure-sqldw-latest

언급URL : https://stackoverflow.com/questions/368351/whats-the-best-way-to-select-the-minimum-value-from-several-columns

반응형