2016-06-10 14 views
2

Ich habe zwei Tabellen und möchte MAX (Datum) von einer Eins-zu-Viele-Tabelle erhalten. Wenn es keinen Wert gibt, sollte es NULL sein. Ich weiß nur, wie ich das mache, indem ich Unterabfragen mache, aber wenn es ~ 20 verschiedene Typen gibt, dann klingen 20 Unterabfragen nicht effizient genug. Gibt es einen besseren Weg?Mehrere MAX-Werte von einer Eins-zu-viele-Tabelle erhalten

 
Table A: 
UserId | Name 
1   | John 
2   | Jane 

Table B: 
UserId | Type | Date 
1  | A  | 2015-01-01 
1  | A  | 2015-12-31 
1  | B  | 2015-01-01 
1  | B  | 2015-12-31 
2  | B  | 2015-06-06 
1  | C  | 2015-01-01 
2  | C  | 2015-09-09 

Result: 
UserId | Type A date | Type B date | Type C date 
1  | 2015-12-31 | 2015-12-31 | NULL 
2  | NULL  | 2015-06-06 | 2015-09-09 
 
Current solution: 

SELECT UserId, 
     (SELECT MAX(Date) FROM B WHERE Type = 'A' AND B.UserId= A.UserId), 
     (SELECT MAX(Date) FROM B WHERE Type = 'B' AND B.UserId= A.UserId), 
     (SELECT MAX(Date) FROM B WHERE Type = 'C' AND B.UserId= A.UserId 
     AND Date > (SELECT MAX(Date) FROM B WHERE Type = 'B' AND B.UserId = A.UserId)) 
    FROM A 

Vielen Dank für alle schnellen Antworten! Sie funktionieren perfekt. Ich habe meine Frage etwas modifiziert, seit ich bemerkt habe, dass ich bei einigen Typen einige Bedingungen hinzufügen muss. Beispielsweise. Typ C nur präsentiert werden sollte, wenn es größer als Typ B.

+0

Typ C mit userId 1 ist null? – Kason

+0

@Kason Ja sollte es NULL sein, da ich das nur präsentieren möchte, wenn Datum größer als Typ B ist. – tep12

+0

warum 2015-09-09 -> 2016-09-09? – Kason

Antwort

3

die erwartete zu bekommen Ergebnis Sie haben nicht einmal zu join müssen, tun einfach ein group by und verwenden case Ausdrücke tun, um bedingte Aggregation:

select userid, 
     max(case when type = 'A' then date end) Adate, 
     max(case when type = 'B' then date end) Bdate 
from tableB 
group by userid 

select a.name, b.Adate, b.Bdate 
from tableA a 
join (select userid, 
      max(case when type = 'A' then date end) Adate, 
      max(case when type = 'B' then date end) Bdate 
     from tableB 
     group by userid) b 
    on a.userid = b.userid 

wenn jedoch die Anzahl der Arten unbekannt ist, würde ich Gruppe durch die Säule geben zu:

wenn Sie auch den Benutzernamen möchten, können Sie tableA mit der obigen Abfrage verbinden. Ich.e Rückgabetypen in verschiedenen Zeilen.

select a.name, b.type, b.date 
from tableA a 
left join (select userid, type, max(date) date, 
      from tableB 
      group by userid, type) b 
    on a.userid = b.userid 
2

Sie können in der SELECT anstelle der Unterabfragen eine bedingte Aggregation tun:

SELECT 
    a.UserId, 
    MAX(CASE WHEN b.Type = 'A' THEN b.date END) AS ADate, 
    MAX(CASE WHEN b.Type = 'B' THEN b.date END) AS BDate 
FROM TableA a 
LEFT JOIN TableB b 
    ON b.UserId = a.UserId 
GROUP BY a.UserId; 

Allerdings wird die oben nur funktionieren, wenn Sie die Werte von Type kennen. Wenn Sie dies nicht tun, müssen Sie eine dynamische Abfrage verwenden.

DECLARE @sql NVARCHAR(MAX) = N''; 

SELECT @sql = 
'SELECT 
    a.UserId' + CHAR(10) + 
(SELECT DISTINCT 
' , MAX(CASE WHEN b.Type = ''' + Type + ''' THEN b.Date END) AS ' + QUOTENAME(Type + 'Date') + CHAR(10) 
FROM TableB 
FOR XML PATH('') 
) + 
'FROM TableA a 
LEFT JOIN TableB b 
    ON b.UserId = a.UserId 
GROUP BY a.UserId;'; 

EXEC (@sql); 

ONLINE DEMO

Referenz: Cross Tabs and Pivots by Jeff Moden

2

Verwenden Dynamische Pivot, egal wie viele Typ Sie haben, müssen Sie nicht die Abfrage ändern müssen.

DECLARE @DynamicPivotQuery AS NVARCHAR(MAX) 
DECLARE @ColumnName AS NVARCHAR(MAX) 

SELECT @ColumnName = ISNULL(@ColumnName + ',','') + QUOTENAME([TYPE]) 
FROM (SELECT DISTINCT [TYPE] as [TYPE] FROM [master].[dbo].[YourTable]) AS TypeTable 

SET @DynamicPivotQuery = 
'SELECT USERID,' + @ColumnName+' 
FROM [master].[dbo].[YourTable] 
PIVOT(MAX([Date]) 
     FOR [TYPE] IN (' + @ColumnName+')) AS PVTTable' 

EXEC sp_executesql @DynamicPivotQuery 

Ergebnis ist unten wie:

USERID A   B 
1  2015-12-31 2015-12-31 
2  NULL  2015-06-06 

Edit: Für Typ C gibt es eine Bedingung:

DECLARE @DynamicPivotQuery AS NVARCHAR(MAX) 
DECLARE @ColumnName AS NVARCHAR(MAX) 
DECLARE @ColumnNameForDisplay AS NVARCHAR(MAX) 
DECLARE @otherCondition AS NVARCHAR(MAX) 

SELECT @ColumnName = ISNULL(@ColumnName + ',','') + QUOTENAME([TYPE]) 
FROM (SELECT DISTINCT [TYPE] as [TYPE] FROM [master].[dbo].[YourTable]) AS TypeTable 

SELECT @ColumnNameForDisplay = ISNULL(@ColumnNameForDisplay + ',','') + [TYPE2] 
FROM (SELECT DISTINCT [TYPE]+' as [Type '+[TYPE]+' date]' as [TYPE2] FROM [master].[dbo].[YourTable] where type <> 'c') AS TypeTable 

SELECT @otherCondition = ' ,Case when c <= b then null else c END as [Type C date] ' 


SET @DynamicPivotQuery = ' 
SELECT USERID,'[email protected][email protected]+' from(
SELECT USERID,' + @ColumnName+' 
FROM [master].[dbo].[YourTable] 
PIVOT(MAX([Date]) 
FOR [TYPE] IN (' + @ColumnName+')) AS PVTTable) as t' 

EXEC sp_executesql @DynamicPivotQuery 

Ergebnis wie unten:

USERID Type A date Type B date Type C date 
1  2015-12-31 2015-12-31 NULL 
2  NULL  2015-06-06 2015-09-09 
+0

Nur zwei Hinweise: Um Ihre Spaltenliste zu erstellen, sollten Sie den Ansatz mit 'STUFF' und' FOR XML PATH ('') 'bevorzugen und es ist erwähnenswert, dass * altmodische Pivot * mit bedingten Aggregaten (' GROUP BY') bietet Ihnen mehr Kontrolle ... Und, * last but not least *, dürfen Sie dies in * ad-hoc-SQL * verwenden.Ihr dynamischer Ansatz muss mit 'EXEC' aufgerufen werden (keine VIEWS, keine Funktionen ...) – Shnugo

Verwandte Themen