99

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、および Col 3の最小値を含む列)?

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アプローチを試してみましたが、少し面倒ですが動作します。同じ行に2つの最小値があるという事実に対応する必要があるため、回答に記載されているよりも複雑です。

とにかく、私の制約を考えると、かなりうまく機能する現在のソリューションを投稿すると思いました。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 列の要件のためだけにすべてのクエリを再設計することはできません)、非常にエレガントな「脱獄」です。カード"。

4

20 に答える 20

67

これを達成するには多くの方法があると思われます。私の提案は、 Case/When を使用してそれを行うことです。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
于 2008-12-15T13:39:23.123 に答える
67

使用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 フィドル

于 2015-04-23T21:35:49.533 に答える
24

MySQL では、これを使用します。

select least(col1, col2, col3) FROM yourtable
于 2014-04-03T09:46:06.047 に答える
11

ひねりを加えた「ブルートフォース」アプローチを使用できます。

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

最初の when 条件が失敗すると、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]

2 つ以上の列の間に同点がある場合は、できるだけ早くステートメント<=を終了するように注意してください。CASE

于 2015-03-02T13:09:44.533 に答える
6

それを行う最善の方法は、おそらくそれを行わないことです。目的の結果を達成するためのはるかに簡単な方法があるのに、意味のある情報を抽出するために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ます。これにより、操作の「コスト」が選択からそれが属する更新/挿入に移動します-私の経験では、ほとんどのデータベーステーブルは書き込みよりもはるかに頻繁に読み取られるため、書き込み時にコストが発生することは、時間の経過とともにより効率的になる傾向があります。

つまり、行の最小値は、他の列の 1 つが変更されたときにのみ変更されるため、選択するたびに計算するのではなく、そのときに計算する必要があります (データが変更されていない場合は無駄になります)。次に、次のようなテーブルになります。

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でより多くのスペースを占有し、挿入と挿入の速度がわずかに遅くなります.更新しますが、選択の場合ははるかに高速になる可能性があります-推奨されるアプローチは優先順位に依存する必要がありますが、前述のように、ほとんどのテーブルは書き込まれるよりもはるかに頻繁に読み取られます.

于 2008-12-15T13:43:17.797 に答える
6

あなたの例のように列が整数の場合、関数を作成します。

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)
于 2012-08-28T13:48:52.927 に答える
5

ユニオン クエリを使用してこれを行うこともできます。列の数が増えると、クエリを変更する必要がありますが、少なくとも単純な変更になります。

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
于 2008-12-15T13:51:25.580 に答える
4

これはブルートフォースですが、機能します

 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() は、列全体ではなく、1 つの列でのみ機能するためです。

于 2008-12-15T13:40:36.193 に答える
2

複数の列の場合は CASE ステートメントを使用するのが最善ですが、2 つの数値列 i と j には簡単な計算を使用できます。

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

この数式は、複数の列の最小値を取得するために使用できますが、2 を超えると非常に面倒です。min(i,j,k) は min(i,min(j,k)) になります。

于 2016-10-11T15:50:54.627 に答える
2

この質問この質問の両方がこれに答えようとしています。

要約すると、Oracleにはこのための組み込み関数があり、Sql Serverでは、ユーザー定義関数を定義するか、caseステートメントを使用するかのいずれかで行き詰まっています。

于 2008-12-16T02:40:20.910 に答える
1

ストアド プロシージャを作成できる場合は、値の配列を取り、それを呼び出すことができます。

于 2008-12-15T13:44:56.543 に答える
1

ユニオン クエリに少しひねりを加えます。

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
于 2011-08-30T20:53:15.057 に答える
1
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
于 2008-12-15T13:46:11.143 に答える
1

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

このようにして、非常に多くの演算子で迷子になることはありません:)

ただし、これは他の選択肢よりも遅くなる可能性があります。

それはあなたの選択です...

于 2008-12-15T15:53:13.947 に答える
1

探している値 (通常はステータス コード) がわかっている場合は、次の情報が役立ちます。

select case when 0 in (PAGE1STATUS ,PAGE2STATUS ,PAGE3STATUS,
PAGE4STATUS,PAGE5STATUS ,PAGE6STATUS) then 0 else 1 end
FROM CUSTOMERS_FORMS
于 2013-08-28T19:47:52.427 に答える