Frage

In der folgenden Tabelle in SQL Server 2005 Gegeben:

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

Was ist der beste Weg, um die Abfrage zu schreiben, die das folgende Ergebnis (dh ein ergibt sich, dass die letzte Spalte ergibt - eine Spalte mit den Minium Werte aus Col1, Col2 und Col 3 für jede Zeile )?

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

UPDATE:

Zur Klarstellung (wie ich in dem coments gesagt habe) in dem realen Szenario ist die Datenbank richtig normalisiert . Diese „Array“ Spalten sind nicht in einer tatsächlichen Tabelle, sondern sind in der Ergebnismenge, die in einem Bericht erforderlich ist. Und die neue Anforderung ist, dass der Bericht auch diese MinValue Spalte benötigt. Ich kann nicht die zugrunde liegende Ergebnismenge ändern und deshalb ich war auf der Suche nach T-SQL für ein handliche „erhält aus dem Gefängnis Karte“.

Ich habe versucht, der CASE-Ansatz unten erwähnt und es funktioniert, obwohl es ein wenig umständlich ist. Es ist auch komplizierter als in den Antworten angegeben, weil Sie für die Tatsache gerecht zu werden müssen, dass es in der gleichen Zeile zwei min Werte sind.

Wie auch immer, dachte ich, dass ich meine aktuelle Lösung veröffentlichen würde meine Zwänge, die, gegeben, recht gut funktioniert. Es nutzt den UNPIVOT Operator:

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

Ich werde im Voraus sagen, dass ich nicht erwarten, dass dies die beste Leistung zu bieten, aber unter den gegebenen Umständen (ich nicht all Abfragen Redesign kann nur für die neue MinValue Spalte Anforderung), es ist ein ziemlich elegant „erhalten aus dem Gefängnis Karte“.

War es hilfreich?

Lösung

Es gibt wahrscheinlich viele Möglichkeiten, um dies zu erreichen. Mein Vorschlag ist, Fall zu verwenden, / Wenn es zu tun. Mit 3 Spalten, dann ist es nicht so schlimm.

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

Andere Tipps

Mit CROSS APPLY:

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

SQL Fiddle

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

Sie können die "Brute-Force" -Ansatz mit einem Twist verwenden:

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

Wenn die erste Bedingung, wenn es nicht garantiert, dass Sp1 nicht der kleinste Wert ist daher können Sie es von restlichen Bedingungen beseitigen. Ebenso für die nachfolgenden Bedingungen. Für fünf Spalten Ihre Abfrage wird:

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]

Beachten Sie, dass, wenn es eine Verbindung zwischen zwei oder mehr Spalten ist dann <= stellt sicher, dass wir die CASE Aussage so früh wie möglich beenden.

Der beste Weg, das zu tun, ist wahrscheinlich nicht , es zu tun - es ist seltsam, dass die Menschen darauf bestehen, ihre Daten in eine Art und Weise zu speichern, die SQL „Gymnastik“ erfordert aussagekräftige Informationen zu extrahieren, wenn es weit einfachere Wege, um das gewünschte Ergebnis zu erzielen, wenn Sie nur Ihr Schema ein wenig besser strukturieren: -)

Die rechts Art und Weise, dies zu tun, meiner Meinung nach, ist die folgende Tabelle haben:

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

mit ID/Col als Primärschlüssel (und möglicherweise Col als zusätzliche Schlüssel, je nach Bedarf). Dann Ihre Anfrage wird zu einem einfachen select min(val) from tbl und Sie können immer noch die einzelnen ‚alten Säulen‘ separat behandeln, indem where col = 2 in Ihren anderen Abfragen. Dies ermöglicht auch eine einfache Erweiterung sollte die Zahl der ‚alte Spalten‘ wachsen.

Das macht Ihre Anfragen so viel einfacher. Die allgemeine Richtlinie I zu verwenden, sind in der Regel ist, wenn Sie immer etwas, das in einer Datenbank Zeile wie ein Array aussieht, sind Sie wahrscheinlich etwas falsch und sollte tun, denken die Daten über die Umstrukturierung.


Wenn jedoch aus irgendeinem Grund Sie kann nicht die Spalten ändern, würde ich vorschlagen, Einfügen und Aktualisieren Trigger verwenden und fügen Sie andere Spalte, die diese Trigger auf die eingestellte Minimum auf Col1/2/3. Dies wird die ‚Kosten‘ der Operation bewegt sich von der Auswahl weg zum Update / Insert, wo es hingehört - die meisten Datenbanktabellen in meiner Erfahrung sind weit häufiger gelesen als so geschrieben tendieren dazu, die Kosten für Schreib entstehen effizienter im Laufe der Zeit zu sein.

Mit anderen Worten ändert sich das Minimum für eine Zeile nur dann, wenn eine der anderen Spalten zu ändern, so das ist , wenn Sie sollten es werden, zu berechnen, nicht jedes Mal, wenn Sie wählen (die, wenn die Daten verschwendet ändert sich nicht). Sie würden dann mit einer Tabelle am Ende wie:

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

Eine andere Option, die Entscheidungen auf select Zeit zu machen hat, ist in der Regel eine schlechte Idee, Performance-weise, da nur die Daten ändern es auf insert / update - die Zugabe einer anderen Säule nimmt mehr Platz in der DB und werden etwas langsamer für die Einsätze und Updates kann aber viel schneller für wählt - der bevorzugte Ansatz sollte es auf Ihre Prioritäten ab, sondern, wie gesagt, die meisten Tabellen gelesen werden far häufiger als sie ‚re geschrieben.

Wenn die Spalten waren ganze Zahlen wie in Ihrem Beispiel würde ich eine Funktion erstellen:

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

dann, wenn ich es verwenden muss ich tun würde:

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

Wenn Sie 5 colums haben dann die oben wird

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)

verwenden:

select least(col1, col2, col3) FROM yourtable

Sie können auch mit einer Union-Abfrage tun. Da die Anzahl der Spalten zu erhöhen, müssten Sie die Abfrage verändern, aber zumindest wäre es eine gerade nach vorne Modifikation sein.

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

Dies ist rohe Gewalt, sondern arbeitet

 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

... weil min () funktioniert nur auf einer Spalte und nicht über mehrere Spalten.

diese Frage Und diese Frage versuchen, diese zu beantworten.

Die Rekapitulation ist, dass Oracle eine eingebaute Funktion für diese, mit SQL Server sind Sie stecken entweder die Definition einer benutzerdefinierten-Funktion oder Fall-Anweisungen.

Wenn Sie in der Lage sind, eine gespeicherte Prozedur zu machen, es könnte eine Reihe von Werten annehmen, und man kann nur so nennen.

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

Eine kleine Drehung auf der Union-Abfrage:

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

Wenn Sie SQL 2005 verwenden Sie etwas ordentlich wie dies tun:

;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

Auf diese Weise kann in so viele Betreiber verloren geht nicht:)

Dies könnte jedoch langsamer als die andere Wahl.

Sie haben die Wahl ...

Im Folgenden werde ich eine temporäre Tabelle verwenden, um die mindestens mehrere Termine zu bekommen. Die ersten temporären Tabellenabfragen Tabellen mehr beigetreten verschiedene Termine zu bekommen (wie auch andere Werte für die Abfrage), die zweite temporäre Tabelle wird dann die verschiedenen Spalten und das minimale Datum wie viele Pässe verwenden, da es Datumsspalte sind.

Dies ist im Wesentlichen wie die Union-Abfrage, die gleiche Anzahl von Durchgängen erforderlich, kann aber effizienter sein (basierend auf Erfahrung, sondern müßte Tests). Effizienz war kein Problem in diesem Fall (8.000 Datensätze). Man könnte Index etc.

--==================== 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

Für mehrere Spalten seine besten eine CASE-Anweisung zu verwenden, jedoch für zwei numerische Spalten i und j können Sie einfache mathematische verwenden:

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

Diese Formel verwendet werden, um den Minimalwert von mehreren Spalten zu erhalten, aber es ist wirklich chaotisch letzten 2 min (i, j, k) wäre min (i, min (j, k))

SELECT [ID],
            (
                SELECT MIN([value].[MinValue])
                FROM
                (
                    VALUES
                        ([Col1]),
                        ([Col1]),
                        ([Col2]),
                        ([Col3])
                ) AS [value] ([MinValue])
           ) AS [MinValue]
FROM Table;

Wenn Sie wissen, welche Werte Sie suchen, in der Regel einem Statuscode kann die folgenden hilfreich sein:

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

Ich weiß, diese Frage ist alt, aber ich war immer noch in der Notwendigkeit der Antwort und war mit anderen Antworten nicht zufrieden, damit ich meine eigenen hatte zu entwickeln, das eine Drehung auf @ paxdiablo's ist answer .


Ich kam vom Land von SAP ASE 16.0, und ich brauchte nur einen Blick auf Statistiken von bestimmten Daten, die in verschiedenen Spalten einer einzigen Zeile gültig gespeichert sind IMHO (sie repräsentieren unterschiedliche Zeiten - wenn die Ankunft von etwas geplant, was es erwartet wurde, wenn die Aktion gestartet und schließlich, was die tatsächliche Zeit war). So hatte ich Spalten in die Reihen der temporären Tabelle umgesetzt und vorgeformten meine Abfrage über diese wie gewöhnlich.

N. B. Nicht die one-size-fits-all-Lösung voraus!

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

Das dauerte etwa 30 Sekunden Quellensatz von 630.000 Zeilen und nur Index-Daten, so dass das Ding nicht läuft in zeitkritischen Prozess, aber für Dinge wie eine Zeitdatum Inspektion oder End-of-the-day-Bericht verwendet Sie könnte in Ordnung sein (aber überprüfen, diese mit Ihren Kollegen oder Vorgesetzten, bitte!). Haupt Bonus dieses Stils für mich war, dass ich leicht mehr / weniger Spalten und ändern Gruppierung, Filterung, usw. verwenden können, vor allem, wenn Daten wurden copyied über.

Die zusätzlichen Daten (columnName, maxes, ...) waren mir bei meiner Suche zu unterstützen, so dass Sie sie nicht benötigen; Ich habe sie hier vielleicht ein paar Ideen zu entfachen: -).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top