Złączenia i podzapytania w SQL

0

Ćwiczę podzapytania i złączenia ale czegoś mi brakuje.

Przykład:
Podaj imiona i nazwiska pracowników, którzy przyjęli zamówienia o największych wartościach w kwartałach lat 1996 i 1997.

Napisałem to tak:

SELECT YEAR([Data Zamówienia]) [Rok], DATEPART(QUARTER, [Data Zamówienia]) [Kwartał], MAX(Wartość) [Wartość]
FROM 
	(
	SELECT z.IDzamówienia [ID], SUM(pz.CenaJednostkowa*pz.Ilość) [Wartość], p.Imię + p.Nazwisko [Pracownik], z.DataZamówienia [Data Zamówienia]
	FROM Zamówienia z 
		INNER JOIN PozycjeZamówienia pz
			ON pz.IDzamówienia = z.IDzamówienia
		INNER JOIN Pracownicy p 
			ON p.IDpracownika = z.IDpracownika
	GROUP BY z.IDzamówienia, p.Imię + p.Nazwisko, z.DataZamówienia) AS war
WHERE YEAR([Data Zamówienia]) BETWEEN '1996' AND '1997'
GROUP BY YEAR([Data Zamówienia]), DATEPART(QUARTER, [Data Zamówienia])

Zapytanie zwraca mi maksymalną wartość dla każdego kwartału dla roku 1996 i 1997, ale nie mam jak wyświetlić jeszcze pracownika. Mam kilka takich przykładów, i zawsze napotykam na identyczny problem. Nie mam "uchwytu" żeby dobrać się do tego czego trzeba.
Byłbym bardzo wdzięczny za wyjaśnienie, ew. rozwiązanie i wyjaśnienie. W każdym razie chciałbym to zrozumieć, bo obstawiam że nie jest to skomplikowane.

Załączam schemat bazy:
https://i.paste.pics/1eebb3e9254fcab9ae92cd381a72a18e.png

1

Można tak:

  1. Robisz sobie agregat:
    id_pracownika, rok||kwartal, wartosc_zamowienia
    pytanie bonusowe: Jak rabat ma się do wartości zamówienia w kontekście ceny jednostkowej/ilości produktu?

  2. Z agregatu wybierasz te id_pracownika, które mają max. w określonym kwartale.

  3. Robisz joina między pracownicy i step#2 (+ jakiegoś distincta, jeśli pracownik został top-pracownikiem Q1,Q2,Q3,Q4, ...)

Można też funkcjami analitycznymi kombinować, ale ćwiczysz coś innego.

0

Udało mi się wpaść na rozwiązanie jeszcze przed sprawdzaniem odpowiedzi. Ale widzę że tok rozumowania był podobny. Po prostu mogę użyć tego samego podzapytania i złączyć je na wartościach. Tyle że wydaje mi się, że rozmiar tego zapytania jest większy niż mógłby być.

SELECT Q2.Pracownik, Q1.Wartość, Q1.Kwartał, Q2.[Data Zamówienia]
FROM (/*zamówienia o maksymalnych wartościach w kwartałach roku 1996 i 1997*/
	SELECT YEAR([Data Zamówienia]) [Rok], DATEPART(QUARTER, [Data Zamówienia]) [Kwartał], MAX(Wartość) [Wartość]
	FROM 
		(/*wartości każdego zamówienia*/
		SELECT z.IDzamówienia [ID], SUM(pz.CenaJednostkowa*pz.Ilość) [Wartość], p.Imię + p.Nazwisko [Pracownik], z.DataZamówienia [Data Zamówienia]
		FROM Zamówienia z 
			INNER JOIN PozycjeZamówienia pz
				ON pz.IDzamówienia = z.IDzamówienia
			INNER JOIN Pracownicy p 
				ON p.IDpracownika = z.IDpracownika
		GROUP BY z.IDzamówienia, p.Imię + p.Nazwisko, z.DataZamówienia) AS war
	WHERE YEAR([Data Zamówienia]) BETWEEN '1996' AND '1997'
	GROUP BY YEAR([Data Zamówienia]), DATEPART(QUARTER, [Data Zamówienia])) AS Q1
		INNER JOIN (/*podzapytanie war*/
					SELECT z.IDzamówienia [ID], SUM(pz.CenaJednostkowa*pz.Ilość) [Wartość], p.Imię + p.Nazwisko [Pracownik], z.DataZamówienia [Data Zamówienia]
					FROM Zamówienia z 
						INNER JOIN PozycjeZamówienia pz
							ON pz.IDzamówienia = z.IDzamówienia
						INNER JOIN Pracownicy p 
							ON p.IDpracownika = z.IDpracownika
					GROUP BY z.IDzamówienia, p.Imię + p.Nazwisko, z.DataZamówienia) AS Q2 ON Q2.Wartość = Q1.Wartość;

Tzn. podzapytanie war jest w Q1, po czym łączę je z Q1 jako Q2. Nie można nadać temu podzapytaniu jakiegoś aliasu, żeby skrócić zapis? Bo poza Q1 war jest niewidoczny.

1

Można użyć tzw. faktoringu, tzn. skorzystać z klauzuli WITH i budować zapytanie w stylu:

WITH agregat AS ( /* Twoje  zapytanie */ )
select a.*,b.* from agregat a inner join tabela b on ...
0

Sprawdzałem wiele możliwości i zawsze daje mi "Incorrect syntax near )" kiedy próbuję użyć WITH. Mam identycznie jak tutaj podałeś.

1

Niepotrzebnie masz tyle tych podzapytań:

 with grpQ as (
SELECT 
	 YEAR([Data Zamówienia]) rok
	 ,DATEPART(QUARTER, [Data Zamówienia]) kw
	 p.Imię + p.Nazwisko [Pracownik]
	 SUM(pz.CenaJednostkowa*pz.Ilość) [Wartość]
FROM 
	Zamówienia z 
    INNER JOIN PozycjeZamówienia pz ON pz.IDzamówienia = z.IDzamówienia
    INNER JOIN Pracownicy p ON p.IDpracownika = z.IDpracownika
WHERE 
	YEAR([Data Zamówienia]) BETWEEN '1996' AND '1997'
GROUP BY 
	 YEAR([Data Zamówienia])
	 ,DATEPART(QUARTER, [Data Zamówienia])
	 p.Imię + p.Nazwisko),
rpt as (
select 
	*
	,rank() over (partition by  rok,kw order by [wartość] desc) r 
from 
	qrpQ)

select * from rpt where r=1
0

No właśnie jak wyżej napisałem u mnie nie działa WITH. Podejrzewałbym zbyt stare Management Studio ale jest to wersja najnowsza.

1

Jaka wersja SQL Servera?

1

Zrób sobie jakiś prosty test na tego WITHa. Może problem jest gdzie indziej :-)

WITH foo(moj_atrybut) as (select 1)
select f.* from foo f;
0

Na PC działa, na laptopie nie. Na obu ta sama wersja. Ale nawet jak użyję WITH to nie pozwala mi go użyć w podzapytaniu.

1

pokaż zapytanie które puszczasz. with musi być pierwsze

0

Nie wiem co jest ze mną nie tak ale ruszyło.

WITH Q1 AS (
SELECT k.NazwaFirmy [Klient]
FROM Klienci k
)

SELECT *
FROM (
SELECT *
FROM Q1) AS Q12

Tyle że jeżeli chciałbym zadeklarować więcej kwerend (być może nie ma to tutaj sensu ale chodzi o przykład), np:

WITH Q1 AS (
SELECT k.NazwaFirmy [Klient]
FROM Klienci k
)
WITH Q2 AS (
SELECT d.NazwaFimy [Dostawca]
FROM Dostawcy d
)
SELECT *
FROM (
SELECT *
FROM Q1,Q2) AS Q12

Pojawia się już seria błędów:

Msg 156, Level 15, State 1, Line 8
Incorrect syntax near the keyword 'WITH'.
Msg 319, Level 15, State 1, Line 8
Incorrect syntax near the keyword 'with'. If this statement is a common table expression, an xmlnamespaces clause or a change tracking context clause, the previous statement must be terminated with a semicolon.
1

Sprawdź sobie składnię: https://docs.microsoft.com/en-us/sql/t-sql/queries/with-common-table-expression-transact-sql?view=sql-server-ver15

W skrócie:

WITH 
Q1 AS ( /* Zapytanie */ ),
Q2 AS ( /* Inne zapytanie*/ )
SELECT a.*, b.* from Q1 a, Q2 b;
0

Dokładnie ten sam błąd. Nawet z przecinkiem (którego wcześniej nie było).

WITH Q1 AS (SELECT k.NazwaFirmy [Klient] FROM Klienci k),
WITH Q2 AS (SELECT d.NazwaFirmy [Dostawca] FROM Dostawcy d)

SELECT Q12.*
FROM (
SELECT a.*, b.*
FROM Q1 a,Q2 b) AS Q12

Wystarczy że użyję WITH drugi raz i wszystko się sypie.

Edit

Dobra, widzę problem. Musi być jeden WITH na całą serię kwerend. Nawet zagnieżdżanie działa bez problemów. To by chyba było na tyle. Dzięki za pomoc.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.