Jak Mogę (lub Mogę) wybrać różne na wielu kolumnach?

Muszę pobrać wszystkie wiersze z tabeli, w której połączone 2 kolumny są różne. Więc chcę wszystkie sprzedaży, które nie mają żadnych innych sprzedaży, które miały miejsce w tym samym dniu za tę samą cenę. Sprzedaż, która jest unikalna w zależności od dnia i ceny, zostanie zaktualizowana do stanu aktywnego.

Tak sobie myślę:

UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
             FROM sales
             HAVING count = 1)
Ale boli mnie mózg.
Author: Erwin Brandstetter, 2008-09-10

4 answers

SELECT DISTINCT a,b,c FROM t

Jest mniej więcej równoważne:

SELECT a,b,c FROM t GROUP BY a,b,c

To dobry pomysł, aby przyzwyczaić się do grupy przez składnię, ponieważ jest bardziej wydajny.

Dla Twojego zapytania, zrobiłbym to tak:

UPDATE sales
SET status='ACTIVE'
WHERE id IN
(
    SELECT id
    FROM sales S
    INNER JOIN
    (
        SELECT saleprice, saledate
        FROM sales
        GROUP BY saleprice, saledate
        HAVING COUNT(*) = 1 
    ) T
    ON S.saleprice=T.saleprice AND s.saledate=T.saledate
 )
 379
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-10 15:55:08

Jeśli zbierzesz odpowiedzi do tej pory, posprzątaj i popraw, dojdziesz do tego nadrzędnego zapytania:

UPDATE sales
SET    status = 'ACTIVE'
WHERE  (saleprice, saledate) IN (
    SELECT saleprice, saledate
    FROM   sales
    GROUP  BY saleprice, saledate
    HAVING count(*) = 1 
    );

Który jest znacznie szybszy niż którykolwiek z nich. W moich testach na PostgreSQL 8.4 i 9.1 sprawdzam poprawność aktualnie akceptowanej odpowiedzi przez współczynnik 10 - 15 (W moich testach na PostgreSQL 8.4 i 9.1).

Ale to wciąż nie jest optymalne. Użyj a NOT EXISTS (anti -) semi-join dla jeszcze lepszej wydajności. EXISTS jest standardowym SQL, istnieje od zawsze (przynajmniej od PostgreSQL 7.2, na długo przed tym pytaniem) i idealnie pasuje do przedstawionych wymagań: {]}

UPDATE sales s
SET    status = 'ACTIVE'
WHERE  NOT EXISTS (
   SELECT 1
   FROM   sales s1
   WHERE  s.saleprice = s1.saleprice
   AND    s.saledate  = s1.saledate
   AND    s.id <> s1.id                     -- except for row itself
   );
AND    s.status IS DISTINCT FROM 'ACTIVE';  -- avoid empty updates. see below

SQL Fiddle.

Unikalny klucz do identyfikacji wiersza

Jeśli nie masz klucza głównego lub unikalnego dla tabeli (id w przykładzie), możesz zastąpić kolumną systemową ctid dla celów tego zapytania (ale nie dla niektórych innych celów):

   AND    s1.ctid <> s.ctid

każdy stół powinien mieć klucz podstawowy. Dodaj jeden, jeśli jeszcze go nie masz. Proponuję serial lub IDENTITY kolumnę w Postgres 10+.

Powiązane:

Jak to jest szybciej?

Subquery w EXISTS (anty-)Semi-join mogą przestać Oceniać, gdy tylko znajdzie się pierwszy dupe (nie ma sensu szukać dalej). W przypadku tabeli bazowej z kilkoma duplikatami jest to tylko nieznacznie bardziej wydajne. Z dużą ilością duplikatów staje się to sposób bardziej efektywny.

Wyklucz puste aktualizacje

Jeśli niektóre lub wiele wierszy ma już status = 'ACTIVE', twoja aktualizacja niczego nie zmieni, ale nadal wstawia nową wersję wiersza za pełnym kosztem (obowiązują drobne wyjątki). Normalnie tego nie chcesz. Dodaj kolejnyWHERE warunek, jak pokazano powyżej, aby to jeszcze szybciej:

Jeśli status jest zdefiniowane NOT NULL, można uprościć do:

AND status <> 'ACTIVE';

Subtelna różnica w obsłudze NULL

To zapytanie (w przeciwieństwie do obecnie akceptowana odpowiedź przez Joela ) nie traktuje wartości NULL jako równych. Te dwa rzędy dla (saleprice, saledate) kwalifikowałyby się jako " odrębne "(choć wyglądające identycznie jak ludzkie oko): {]}

(123, NULL)
(123, NULL)

Również przechodzi w unikalnym indeksie i prawie wszędzie indziej, ponieważ wartości NULL nie są równe zgodnie ze standardem SQL. Zobacz:

Otoh, GROUP BY lub DISTINCT lub DISTINCT ON () traktują wartości NULL jako równe. Użycie odpowiedni styl zapytań w zależności od tego, co chcesz osiągnąć. Możesz nadal używać tego szybszego stylu zapytań za pomocą IS NOT DISTINCT FROM zamiast = dla dowolnych lub wszystkich porównań, aby null było równe. Więcej:

Jeśli wszystkie porównywane kolumny są zdefiniowane NOT NULL, nie ma miejsca na nieporozumienia.

 295
Author: Erwin Brandstetter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-13 12:24:49

Problem z zapytaniem polega na tym, że gdy używasz klauzuli GROUP BY (co zasadniczo robisz, używając distinct), możesz używać tylko kolumn, które grupujesz lub agregujesz funkcje. Nie można użyć identyfikatora kolumny, ponieważ istnieją potencjalnie różne wartości. W Twoim przypadku zawsze jest tylko jedna wartość ze względu na klauzulę HAVING, ale większość RDBM nie jest wystarczająco inteligentna, aby to rozpoznać.

To powinno jednak działać (i nie wymaga połączenia):

UPDATE sales
SET status='ACTIVE'
WHERE id IN (
  SELECT MIN(id) FROM sales
  GROUP BY saleprice, saledate
  HAVING COUNT(id) = 1
)

Możesz też użyć Maxa lub AVG zamiast MIN, ważne jest tylko użycie funkcji, która zwraca wartość kolumny, jeśli jest tylko jeden pasujący wiersz.

 22
Author: Christian Berg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-10 16:17:13

Chcę wybrać różne wartości z jednej kolumny 'GrondOfLucht', ale powinny być sortowane w kolejności podanej w kolumnie 'sortowanie'. Nie mogę uzyskać odrębnych wartości tylko jednej kolumny używając

Select distinct GrondOfLucht,sortering
from CorWijzeVanAanleg
order by sortering

Da również kolumnę "sortowanie", a ponieważ "GrondOfLucht" i "sortowanie" nie są unikalne, wynikiem będą wszystkie wiersze.

Użyj grupy, aby wybrać rekordy 'GrondOfLucht' w kolejności podanej przez ' sorting

SELECT        GrondOfLucht
FROM            dbo.CorWijzeVanAanleg
GROUP BY GrondOfLucht, sortering
ORDER BY MIN(sortering)
 1
Author: frans eilering,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-13 10:41:47