none
Nur eine Zeile aus der rechten Join Tabelle RRS feed

  • Frage

  • Guten Morgen,

    hier erstmal meine Datenstruktur:

    CREATE TABLE [dbo].[Test](
    	[Id] [int] IDENTITY(1,1) NOT NULL,
    	[Name] [nvarchar](55) NOT NULL,
    	[Beginn] [datetime] NOT NULL,
    	[Ende] [datetime] NOT NULL
    ) ON [PRIMARY]
    GO
    SET IDENTITY_INSERT [dbo].[Test] ON 
    GO
    INSERT [dbo].[Test] ([Id], [Name], [Beginn], [Ende]) VALUES (1, N'Montag', CAST(N'2018-03-26T00:00:00.000' AS DateTime), CAST(N'2018-03-27T00:00:00.000' AS DateTime))
    GO
    INSERT [dbo].[Test] ([Id], [Name], [Beginn], [Ende]) VALUES (2, N'Dienstag', CAST(N'2018-03-27T00:00:00.000' AS DateTime), CAST(N'2018-03-28T00:00:00.000' AS DateTime))
    GO
    INSERT [dbo].[Test] ([Id], [Name], [Beginn], [Ende]) VALUES (3, N'Mittwoch', CAST(N'2018-03-28T00:00:00.000' AS DateTime), CAST(N'2018-03-29T00:00:00.000' AS DateTime))
    GO
    INSERT [dbo].[Test] ([Id], [Name], [Beginn], [Ende]) VALUES (4, N'Donnerstag', CAST(N'2018-03-29T00:00:00.000' AS DateTime), CAST(N'2018-03-30T00:00:00.000' AS DateTime))
    GO
    INSERT [dbo].[Test] ([Id], [Name], [Beginn], [Ende]) VALUES (5, N'Freitag', CAST(N'2018-03-30T00:00:00.000' AS DateTime), CAST(N'2018-03-31T00:00:00.000' AS DateTime))
    GO
    SET IDENTITY_INSERT [dbo].[Test] OFF

    Hier mein Select:

    Select T1.Name, T1.Beginn, T1.Ende, T2.Name as NameNaechster, T2.Beginn as BeginnNaechster, T2.Ende as EndeNaechster from Test T1
    LEFT OUTER JOIN Test T2 ON T2.Beginn>T1.Ende
    WHERE T1.Name='Montag'

    Ich möchte erreichen dass ich einen bestimmten Datensatz auswähle und zeitlich gesehen (Beginn > Ende) immer geneu den nächsten Datensatz auch erhalte. Existiert er nicht sollen die Felder NULL sein.

    Mir gelingt die Einschränkung auf einen Datensatz nicht. Mit dem Aktuellen Select erhalte ich insgesamt drei Zeilen obwohl es nur eine sein dürfte.

    Dass die Wochentage im Beispiel aufsteigend sind, ist nur ein konstuierter Zufall.

    Kann mir jemand helfen?

    Viele Grüße

    Thorsten

    Montag, 26. März 2018 05:22

Antworten

Alle Antworten

  • Hallo Thorsten,

    wenn es immer nur ein Datensatz sein soll, reicht ein einfaches TOP 1 mit entsprechender Sortierung aus

    Select TOP 1 T1.Name, T1.Beginn, T1.Ende, T2.Name as NameNaechster, T2.Beginn as BeginnNaechster, T2.Ende as EndeNaechster 
    from #Test AS T1
         LEFT OUTER JOIN 
         #Test T2 ON T2.Beginn>T1.Ende
    WHERE T1.Name='Montag'
    ORDER BY T2.Beginn


    Olaf Helper

    [ Blog] [ Xing] [ MVP]

    Montag, 26. März 2018 05:52
  • Hallo Olaf,

    TOP hatte ich schon probiert, aber auf der Rechten Seite soll es nur ein Datensatz sein, nämlich der nächste im Zeitablauf. Links ist die Anzahl variabel.

    Montag, 26. März 2018 06:33
  • Hallo nochmal,

    das beste Ergebnis habe ich bisher mit CTE hinbekommen:

    WITH cte1 (Name, Beginn,Ende) AS
    (
       select Name, Beginn,Ende from Test where Name='Montag'
    ),
    
    cte2 (Name, Beginn,Ende) AS
    (
       select top(1) Name, Beginn,Ende from Test  order by Beginn
    )
    
    SELECT *
    FROM cte2 
       CROSS JOIN cte1 

    Was mir noch fehlt ist die Enschränkung in cte2 das Beginn > cte1.Ende sein soll. Ich habe hier ein schlaues Buch, in dem steht dass man immer auf die vorherige CTE zugreifen kann. Also schreibe ich:

    WITH cte1 (Name, Beginn,Ende) AS
    (
       select Name, Beginn,Ende from Test where Name='Montag'
    ),
    
    cte2 (Name, Beginn,Ende) AS
    (
       select top(1) Name, Beginn,Ende from Test WHERE Beginn>=cte1.Ende order by Beginn
    )
    
    SELECT *
    FROM cte2 
       CROSS JOIN cte1 


    erhalte dann aber den Fehler:

    Der mehrteilige Bezeichner "cte1.Ende" konnte nicht gebunden werden.

    Falls jemand den entscheidenden Tipp hat...

    Viele Grüße

    Thorsten

    Montag, 26. März 2018 08:38
  • Ein Zugriff auf die CTE-Tabellen ist nur mittels "from" möglich, da sie wie Tables/Views fungieren.
    Also

    with cte1 (......) as (....)
    , cte2 (....) as (select * from cte1 ....)

    Dies hilft dir in deinem Fall leider auch nicht.
    Du kannst hier ggf. mit einem skalaren Subselect arbeiten (kleinstes Ende, dass >= Begin ist):

    with cte1 (Name, Begin, Ende, B_Ende) as (
    select Name, Begin, Ende
    ,(select min(Ende) from Test b where a.Begin >= b.Ende) as B_Ende
    from Test a
    where ...
    )

    Select * from CTE1
    left join Test B on B_Ende = B.Ende

    Montag, 26. März 2018 09:03
  • Hallo Eclere,

    das kannst Du mit einem OUTER APPLY sehr schön realisieren:

    SELECT	L.Name,
    		L.Beginn,
    		L.Ende,
    		N.*
    FROM	dbo.Test AS L
    		OUTER APPLY
    		(
    			SELECT	TOP (1)
    					Name	AS	NameNaechster,
    					Beginn	AS	BeginnNaechster,
    					Ende	AS	EndeNaechster
    			FROM	dbo.Test
    			WHERE	Beginn > L.Ende
    			ORDER BY
    					Beginn
    		) AS N
    WHERE	L.Name = 'Freitag';
    GO
    

    Mehr Details zu CROSS APPLY und OUTER APPLY hier!

    https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/


    Uwe Ricken (Blog | Twitter)
    Microsoft Certiied Master - SQL Server 2008
    Microsoft Certified Solution Master - CHARTER Data Platform
    Microsoft Certified Solution Expert - Data Platform
    db Berater GmbH
    Microsoft SQL Server Blog (german only)

    Montag, 26. März 2018 13:30
  • Hallo Zusammen,

    die OUTER APPLY Lösung von Uwe funktioniert wunderbar - Danke wieder etwas gelernt!

    Allerding denke ich, dass aufgrund der "überschneidenden" Daten diese Daten auf kurz vor Mitternacht stehen sollten oder halt die Bedingung (in diesem Fall)

    WHERE Beginn >= L.Ende
    

    lauten sollte.

    Liebe Grüße Volker

    Dienstag, 27. März 2018 07:10
  • Hallo,

    sorry für die späte Rückmeldung. Ich war ein paar Tage weg.

    Outer Apply habe ich auch noch nie gehört, funktioniert aber super.

    Vielen Dank.

    Mittwoch, 11. April 2018 09:47