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

Printed on: December 10, 2024 at 15:12 o'clock

firefly
firefly Aug 28, 2024 at 11:55:17 (UTC)
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 Aug 28, 2024 at 12:02:05 (UTC)
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 Aug 28, 2024 at 12:17:31 (UTC)
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 Aug 29, 2024 at 17:47:33 (UTC)
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 Sep 03, 2024 at 08:51:46 (UTC)
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.