/ / SQL Server: Łączenie krzyżowe w GreenPlum (wersja Pre-LATERAL Postgres) - sql-server, greenplum

SQL Server: Cross Join w GreenPlum (wersja Postgres w wersji przed LATERALNEJ) - sql-server, greenplum

Próbuję przekonwertować następujące zapytanie SQL Server na wersję GreenPlum zapytania:

INSERT INTO #TMP1 (part_id, file_id, location, measure_date)
SELECT DISTINCT
pt.part_id, qf.file_id, qf.edl_desc, pt.measure_date
FROM
part pt WITH (NOLOCK)
INNER JOIN
file_model qm with (nolock) on qm.file_model_id = pt.file_model_id
INNER JOIN
file qf with (nolock) on qf.file_id = qm.file_id;

INSERT INTO @part_list (file_id, part_id, measure_date)
SELECT DISTINCT
t1.file_id, k.part_id, k.measure_date
FROM
#TMP1 t1 WITH (NOLOCK)
CROSS APPLY
(SELECT DISTINCT TOP (300)
t2.part_id, t2.measure_date
FROM
#TMP1 t2 WITH (NOLOCK)
WHERE
t1.file_id = t2.file_id and t1.location = t2.location
ORDER BY
t2.measure_date DESC) k
WHERE
t1.measure_date >= dateadd(day, 30, getdate());

Pomysł polega na tym, że ostateczny stół zawiera najnowsze do 300 części dla wszystkich programów części, które były aktywne (tj. Coś wyprodukowano) w ciągu ostatnich 30 dni.

Za odpowiedzi na to pytanie, Jestem świadomy, że LATERAL JOIN by to zrobił, ale moja organizacja używa starszej wersji Postgres, która nie ma LATERALNEGO, więc zamiast tego zostałem zaimplementowany następującą funkcję:

CREATE FUNCTION BuildActiveParts(p_day INT, p_n INT)
RETURNS SETOF RECORD --TABLE (part_id bigint,file_id int, measure_date timestamp, location varchar(255))
AS $$
DECLARE
part_active RECORD;
part_list RECORD;
BEGIN
FOR part_active IN
SELECT DISTINCT qf.file_id, qf.location
FROM part pt
INNER JOIN file_model qm  on qm.file_model_id = pt.file_model_id
INNER JOIN file qf  on qf.file_id = qm.file_id WHERE pt.measure_date >=  current_date - p_day LOOP
FOR part_list IN
SELECT DISTINCT pt.part_id, qf.file_id, pt.measure_date, qf.location
FROM part pt
INNER JOIN file_model qm  on qm.file_model_id = pt.file_model_id
INNER JOIN file qf  on qf.file_id = qm.file_id WHERE qf.file_id = part_active.file_id
AND qf.location = part_active.location
ORDER BY pt.measure_date DESC LIMIT p_n LOOP
RETURN NEXT part_list;
END LOOP;
END LOOP;
END
$$ LANGUAGE plpgsql;

-- Later used in:

--Build list of all active programs in last p_day days.  This temporary table is a component of a larger function that produces a table based on this and other other calculations, called daily.
-- Note: this insert yields "function cannot execute because it accesses relation"
INSERT INTO TMP_part_list ( part_id, file_id, measure_date,  location)
SELECT DISTINCT * FROM BuildActiveParts(p_day, p_n) AS active_parts (part_id int, file_id text, measure_date timestamp, location text )
;

Niestety ta funkcja jest stosowana we wkładkachdo innej tabeli (nieunikniona rzeczywistość moich wymagań biznesowych), więc chociaż funkcja zwraca ładne, szczęśliwe wyniki, gdy jest uruchomiona w izolacji, bardzo się złoszczę function cannot execute on segment because it accesses relation kiedy próbuję go używać zgodnie z jego przeznaczeniem.Chociaż widziałem sugestie dotyczące efektu „zamiast tego wykonaj VIEW”, nie jest to tak naprawdę opcja, ponieważ przeglądanie widoku wynikającego ze skryptu, którego częścią jest ta funkcja, zajęłoby zbyt dużo czasu.

Co mogę zrobić, poza wyruszeniem na miesięczną wycieczkę przez dżunglę biurokracji, aby przekonać moją organizację, aby zaktualizowała swoje rzeczy, aby rozwiązać ten problem?

Edycja: Oto kilka prób opartych na komentarzach:

Próba z funkcją, nie zadziałała z powodu function cannot execute on segment because it accesses relation:

DROP FUNCTION IF EXISTS BuildRecentParts(TEXT, TEXT, INT);

CREATE FUNCTION BuildRecentParts(file_id TEXT, location_in TEXT, p_n INT)
RETURNS SETOF RECORD --TABLE (measure_date timestamp, part_id bigint)
AS $$
DECLARE
part_list RECORD;
BEGIN
FOR part_list IN
SELECT DISTINCT pt.measure_date, pt.part_id
FROM part pt
INNER JOIN file_model qm  on qm.file_model_id = pt.file_model_id
INNER JOIN file qf  on qf.file_id = qm.file_id
WHERE qf.file_id = file_id
AND qf.edl_desc = location_in
ORDER BY pt.measure_date DESC LIMIT p_n LOOP
RETURN NEXT part_list;
END LOOP;
END
$$ LANGUAGE plpgsql;

SELECT DISTINCT qf.file_id, qf.edl_desc, (SELECT  pti.measure_date, pti.part_id FROM part pti
INNER JOIN file_model qmi  on qmi.file_model_id = pti.file_model_id
INNER JOIN file qfi  on qfi.file_id = qmi.file_id
WHERE qfi.file_id = qf.file_id
AND qfi.edl_desc = qf.edl_desc
ORDER BY pti.measure_date DESC LIMIT 300)
FROM part pt
INNER JOIN file_model qm  on qm.file_model_id = pt.file_model_id
INNER JOIN file qf  on qf.file_id = qm.file_id
WHERE pt.measure_date >=  current_date - 30 ;

Próba bez funkcji nie zadziała, ponieważ podzapytanie ma wiele kolumn:

CREATE TEMPORARY TABLE TMP_TMP1 (part_id bigint, file_id varchar(255), location varchar(255), measure_date timestamp) DISTRIBUTED BY (part_id);

INSERT INTO TMP_TMP1 (part_id, file_id, location, measure_date)
SELECT DISTINCT pt.part_id, qf.file_id, qf.edl_desc, pt.measure_date
FROM part pt
INNER JOIN file_model qm  on qm.file_model_id = pt.file_model_id
INNER JOIN file qf  on qf.file_id = qm.file_id;
ANALYZE TMP_TMP1;


SELECT DISTINCT t1.file_id, t1.location, (SELECT  t2.measure_date, t2.part_id FROM TMP_TMP1 t2
WHERE t2.file_id = t1.file_id
AND t2.location = t1.location
ORDER BY t2.measure_date DESC LIMIT 300)
FROM TMP_TMP1 t1
WHERE t1.measure_date >=  current_date - 30;

Podjąłem również próbę rekurencyjnego CTE, ale okazało się, że nie jest to obsługiwane.

Odpowiedzi:

0 dla odpowiedzi № 1

Pomiędzy odpowiedziami tutaj i od architektów na moimzdecydowaliśmy, że natrafiliśmy na ograniczenie GreenPlum, które byłoby zbyt kosztowne do pokonania, logika wykonująca łączenie krzyżowe zostanie przeniesiona do skryptu języka R, który wywołuje procedurę składowaną, której częścią byłaby ta funkcja.


0 dla odpowiedzi nr 2

Cóż, Greenplum nie ma brudnych odczytów, więc nie możesz zaimplementować wskazówki nolock, którą masz. To też prawdopodobnie dobra rzecz. Poleciłbym również usunięcie tego z SQL Server.

Myślę, że najlepszym rozwiązaniem jest użycie narzędzia analitycznegofunkcja zamiast tej funkcji lub nawet skorelowanego podzapytania, które obsługuje Greenplum. Takie podejście jest również bardziej wydajne w SQL Server.

SELECT sub2.part_id, sub2.location, sub2.measure_date
FROM    (
SELECT sub1.part_id, sub1.location, sub1.measure_date, row_number() over(partition by sub1.part_id order by sub1.measure_date desc) as rownum
FROM    (
SELECT pt.part_id, qf.edl_desc as location, pt.measure_date
FROM part pt
INNER JOIN file_model qm on qm.file_model_id = pt.file_model_id
INNER JOIN file qf on qf.file_id = qm.file_id
WHERE pt.measure_date >=  (now() - interval "30 days")
GROUP BY pt.part_id, qf.edl_desc, pt.measure_date
) AS sub1
) as sub2
WHERE sub2.rownum <= 300;

Teraz musiałem odgadnąć twoje dane, ponieważ wyglądająna przykład możesz mieć problemy z pierwotnym zapytaniem, jeśli masz wiele wartości qf.qcc_file_desc, ponieważ oryginalna grupa zawiera to. Gdybyś miał wiele wartości, sprawy stałyby się brzydkie.

Nie mam też 100% pewności co do funkcji row_number bez znajomości Twoich danych. Zamiast tego może być tak:

row_number() over(partition by sub1.part_id, sub1.location order by sub1.measure_date desc)