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:
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
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
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
Please also mark the comments that contributed to the solution of the article
Content-ID: 667703
Url: https://administrator.de/contentid/667703
Printed on: December 10, 2024 at 15:12 o'clock
5 Comments
Latest comment
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:
Wenn es nur das "S" sein soll:
Ergebnis:
Wenn es über die gesamte Tabelle sein soll:
Ergebnis:
Leider hast du deine Anfrage nicht sehr genau formuliert, aber ich hoffe, ich konnte helfen.
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.
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;
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:
Bei mehr als drei skaliert das nicht so toll, da müsste man sich dann eventuell was anderes ausdenken mit einem Zähler.
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
Also das ist jetzt vielleicht etwas kompliziert, quasi in vier Zügen zum Ziel:
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.
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