timxpp
Goto Top

SQL Abfrage wenn 3 mal hintereinander

Hallo zusammen,

ich bin neu hier und benötige Hilfe bezüglich einer SQL-Abfrage.
Ich muss gleich dazu sagen dass mein SQL-Wissen sich in Grenzen hält.

Nun zu meinem Problem.
Meine SQL-Tabelle sieht wie folgt aus:
Datum	Wert	Art
15.01.2023	15	S
27.01.2023	22	W
01.03.2023	29	S
18.03.2023	33	S
01.05.2023	50	U
08.05.2023	55	S
17.06.2023	65	S
29.06.2023	67	W
01.08.2023	80	S
17.08.2023	89	S
29.09.2023	95	U
01.06.2023	60	S


Ich benötige eine Abfrage die nur ein Ergebnis liefert wenn drei mal "S" zeitlich hintereinander vorkommen.
Also in diesem Fall wäre das Ergebnis
08.05.2023	55	S
01.06.2023	60	S
17.06.2023	65	S

Was noch erschwerend ist, die Spalte mit Datum ist nicht zeitlich sortiert.

Es wäre super, wenn jemand helfen könnte.

Vielen Dank im Voraus.
Tim

Content-ID: 667703

Url: https://administrator.de/contentid/667703

Ausgedruckt am: 21.11.2024 um 20:11 Uhr

firefly
firefly 28.08.2024 um 13:55:17 Uhr
Goto Top
Hi,

Leider hast du nicht angegeben, für welche SQL Datenbank du das brauchst. Wenn wir von PostgreSQL ausgehen, würde das so aussehen:

Basis:

CREATE TABLE sequences_table (
    event_date DATE,
    numeric_value NUMERIC,
    letter CHAR(1)
);

INSERT INTO sequences_table (event_date, numeric_value, letter)
VALUES 
  ('2023-01-15', 15, 'S'),  
  ('2023-01-27', 22, 'W'),  
  ('2023-03-01', 29, 'S'),  
  ('2023-03-18', 33, 'S'),  
  ('2023-05-01', 50, 'U'),  
  ('2023-05-08', 55, 'S'),  
  ('2023-06-01', 60, 'S'),  
  ('2023-06-17', 65, 'S'),  
  ('2023-06-29', 67, 'W'),  
  ('2023-08-01', 80, 'S'),  
  ('2023-08-17', 89, 'S'),  
  ('2023-09-29', 95, 'U');  

Ich benötige eine Abfrage die nur ein Ergebnis liefert wenn drei mal "S" zeitlich hintereinander vorkommen.

Wenn es nur das "S" sein soll:

SELECT event_date, numeric_value, letter
FROM (
  SELECT event_date,
         numeric_value,
         letter,
         LAG(letter, 1) OVER(ORDER BY event_date) AS prev_letter,
         LEAD(letter, 1) OVER(ORDER BY event_date) AS next_letter
  FROM sequences_table
) AS subquery 
WHERE letter = 'S' AND prev_letter = 'S' AND next_letter = 'S';  

Ergebnis:

2023-06-01	60	S

Wenn es über die gesamte Tabelle sein soll:

SELECT event_date, numeric_value, letter
FROM (
  SELECT event_date,
    numeric_value,
    letter,
    ROW_NUMBER() OVER (PARTITION BY letter ORDER BY event_date) as row
  FROM sequences_table
) t
WHERE t.row = 1

Ergebnis:

2023-01-15	15	S
2023-05-01	50	U
2023-01-27	22	W

Leider hast du deine Anfrage nicht sehr genau formuliert, aber ich hoffe, ich konnte helfen.

face-smile
Kraemer
Kraemer 28.08.2024 um 14:02:05 Uhr
Goto Top
WITH sequenced_table AS (
  SELECT *, ROW_NUMBER() OVER (ORDER BY Datum) AS rn
  FROM deine_tabelle
)
SELECT t1.Datum, t1.Wert, t1.Art
FROM sequenced_table t1
JOIN sequenced_table t2 ON t1.rn = t2.rn - 1
JOIN sequenced_table t3 ON t1.rn = t3.rn - 2
WHERE t1.Art = 'S' AND t2.Art = 'S' AND t3.Art = 'S'  
ORDER BY t1.Datum;
ukulele-7
ukulele-7 28.08.2024 um 14:17:31 Uhr
Goto Top
Das ist leider nicht ganz einfach für einen Einsteiger aber natürlich möglich. Die Schwierigkeit besteht darin, das man sich in SQL zunächst nicht so einfach in einem Datensatz in einer Zeile auf einen anderen Datensatz in einer anderen Zeile bezieht, mit Joins oder Window-Functions geht das aber. Welches SQL kommt zum Einsatz?

Bei "nur" drei aufeinander folgenden Art = S würde ich es so lösen:
SELECT	t.*
FROM	(

SELECT	*,
		(	CASE
			WHEN	Art = 'S'  
			AND	(	lag(Art,1) OVER (ORDER BY Datum) = 'S'  
			OR		lead(Art,2) OVER (ORDER BY Datum) = 'S' )  
			AND	(	lag(Art,2) OVER (ORDER BY Datum) = 'S'  
			OR		lead(Art,1) OVER (ORDER BY Datum) = 'S' )  
			THEN	1
			ELSE	0
			END ) AS zeigen
FROM	tabelle

	) t
WHERE	t.zeigen = 1
ORDER BY t.Datum
Bei mehr als drei skaliert das nicht so toll, da müsste man sich dann eventuell was anderes ausdenken mit einem Zähler.
Timxpp
Timxpp 29.08.2024 um 19:47:33 Uhr
Goto Top
Vielen Dank für die schnellen Rückmeldungen, das ist echt super!
Es handelt sich um eine Oracle-Datenbank.

Die Lösung von ukulele-7 habe ich schon getestet und es funktioniert super.
Könnte diese Lösung noch angepasst werden damit die Auswertung nur die 3 neuesten "S" nach Datum ausgibt.
So werden alle dreier Ergebnisse ausgegeben, ich benötige aber nur die neuesten davon.

Viele Grüße und noch mal vielen Dank an alle
Tim
ukulele-7
ukulele-7 03.09.2024 um 10:51:46 Uhr
Goto Top
Also das ist jetzt vielleicht etwas kompliziert, quasi in vier Zügen zum Ziel:
SELECT	t3.*
FROM	(

SELECT	t2.*,
		ROW_NUMBER() OVER (PARTITION BY t2.gruppe ORDER BY t2.Datum DESC) AS zeile
FROM	(

SELECT	t1.*,
		sum(t1.wechsel) OVER (ORDER BY t1.Datum) AS gruppe
FROM	(

SELECT	*,
		(	CASE
			WHEN	Art = 'S'  
			AND	(	lag(Art,1) OVER (ORDER BY Datum) = 'S'  
			OR		lead(Art,2) OVER (ORDER BY Datum) = 'S' )  
			AND	(	lag(Art,2) OVER (ORDER BY Datum) = 'S'  
			OR		lead(Art,1) OVER (ORDER BY Datum) = 'S' )  
			THEN	1
			ELSE	0
			END ) AS zeigen,
		(	CASE
			WHEN	Art = 'S'  
			AND	(	lag(Art) OVER (ORDER BY Datum) IS NULL
			OR		lag(Art) OVER (ORDER BY Datum) != 'S' )  
			THEN	1
			ELSE	0
			END ) AS wechsel
FROM	tabelle

	) t1
WHERE	t1.zeigen = 1

	) t2

	) t3
WHERE	t3.zeile <= 3
ORDER BY t3.Datum
Es funktioniert aber bei mir. Möglich, das man sich irgendwo noch einen Durchlauf / Subquery sparen kann aber eventuell geht es auch nicht mit weniger. Wenn die Laufzeit okay ist dann passt das so.