සෑම GROUP BY කාණ්ඩයකම පළමු පේළිය තෝරන්න?


1365

මාතෘකාවට අනුව, a සමඟ කාණ්ඩ කර ඇති සෑම පේළි සමූහයකම පළමු පේළිය තෝරා ගැනීමට මම කැමතියි GROUP BY.

විශේෂයෙන්, මට මේ වගේ purchasesමේසයක් තිබේ නම්:

SELECT * FROM purchases;

මගේ ප්‍රතිදානය:

id | පාරිභෝගික | සමස්ත
--- + ---------- + ------
 1 | ජෝ | 5
 2 | සැලි | 3
 3 | ජෝ | 2
 4 | සැලි | 1

එක් එක් විසින් කරන ලද idවිශාලතම මිලදී ගැනීම ( total) සඳහා විමසීමට මම කැමතියි customer. මේ වගේ දෙයක්:

SELECT FIRST(id), customer, FIRST(total)
FROM  purchases
GROUP BY customer
ORDER BY total DESC;

අපේක්ෂිත ප්‍රතිදානය:

පළමු (හැඳුනුම්පත) | පාරිභෝගික | පළමු (මුළු)
---------- + ---------- + -------------
        1 | ජෝ | 5
        2 | සැලි | 3

ඔබ සොයන්නේ එක් එක් විශාලතම එක පමණක් බැවින්, විමසන්නේ නැත්තේ ඇයි MAX(total)?
phil294

7
max phil294 උපරිම (මුළු) සඳහා විමසීමෙන් එම එකතුව එය සිදු වූ පේළියේ 'id' අගය සමඟ සම්බන්ධ නොවේ.
gwideman

Answers:


1147

ඔරකල් 9.2+ හි (මුලින් සඳහන් කළ පරිදි 8i + නොවේ), SQL සේවාදායකය 2005+, PostgreSQL 8.4+, DB2, ෆයර්බර්ඩ් 3.0+, ටෙරාඩාටා, සයිබේස්, වර්ටිකා:

WITH summary AS (
    SELECT p.id, 
           p.customer, 
           p.total, 
           ROW_NUMBER() OVER(PARTITION BY p.customer 
                                 ORDER BY p.total DESC) AS rk
      FROM PURCHASES p)
SELECT s.*
  FROM summary s
 WHERE s.rk = 1

ඕනෑම දත්ත සමුදායකින් සහය දක්වයි:

නමුත් සබඳතා බිඳ දැමීම සඳහා ඔබ තර්කනය එකතු කළ යුතුය:

  SELECT MIN(x.id),  -- change to MAX if you want the highest
         x.customer, 
         x.total
    FROM PURCHASES x
    JOIN (SELECT p.customer,
                 MAX(total) AS max_total
            FROM PURCHASES p
        GROUP BY p.customer) y ON y.customer = x.customer
                              AND y.max_total = x.total
GROUP BY x.customer, x.total

2
ඉන්ෆොමික්ස් 12.x කවුළු ක්‍රියාකාරිත්වයට ද සහාය දක්වයි (CTE ව්‍යුත්පන්න වගුවකට පරිවර්තනය කළ යුතු වුවද). ෆයර්බර්ඩ් 3.0 කවුළු කාර්යයන් සඳහා ද සහාය වනු ඇත
a_horse_with_no_name

37
ROW_NUMBER() OVER(PARTITION BY [...])තත්පර 30 සිට මිලි තත්පර කිහිපයක් දක්වා විමසුමක් ලබා ගැනීමට මට තවත් ප්‍රශස්තිකරණයන් උපකාර විය. ස්තූතියි! (PostgreSQL 9.2)
සෑම්

8
totalඑක් ගනුදෙනුකරුවකුට සමානව ඉහළම මිලට ගත් බහුවිධ මිලදී ගැනීම් තිබේ නම් , 1 වන විමසුම අත්තනෝමතික ජයග්‍රාහකයෙකු ලබා දෙයි (ක්‍රියාත්මක කිරීමේ විස්තර මත පදනම්ව; idසෑම ක්‍රියාත්මක කිරීමකටම වෙනස් විය හැකිය!). සාමාන්‍යයෙන් (සෑම විටම නොවේ) ඔබට අවශ්‍ය වන්නේ එක් පාරිභෝගිකයෙකුට එක් පේළියක් වන අතර එය අර්ථ දැක්වෙන්නේ id“ කුඩාම එක” වැනි අතිරේක නිර්ණායකයන් මගිනි. නිවැරදි කිරීමට, ලැයිස්තුවට එකතු idකරන්න . එවිට ඔබට 2 වන විමසුමට සමාන ප්‍රති result ලයක් ලැබෙනු ඇත , එය මෙම නඩුව සඳහා ඉතා අකාර්යක්ෂම වේ. එසේම, සෑම අතිරේක තීරුවකටම ඔබට තවත් අනුකාරකයක් අවශ්‍ය වේ. ORDER BYrow_number()
අර්වින් බ්‍රැන්ඩ්ස්ටෙටර්

2
ගූගල් හි BigQuery පළමු විමසුමේ ROW_NUMBER () විධානයට සහය දක්වයි. අප වෙනුවෙන්
ආකර්ශනීය

2
කවුළු ශ්‍රිතය සහිත පළමු අනුවාදය SQLite අනුවාදය 3.25.0 ලෙස ක්‍රියාත්මක වන බව සලකන්න: sqlite.org/windowfunctions.html#history
brianz

1179

දී PostgreSQL මෙම සාමාන්යයෙන් , සරල හා වේගවත් (පහත වැඩි කාර්ය සාධන ප්රශස්තිකරණය):

SELECT DISTINCT ON (customer)
       id, customer, total
FROM   purchases
ORDER  BY customer, total DESC, id;

හෝ නිමැවුම් තීරුවල සාමාන්‍ය සංඛ්‍යා සමඟ කෙටි (පැහැදිලි නැතිනම්):

SELECT DISTINCT ON (2)
       id, customer, total
FROM   purchases
ORDER  BY 2, 3 DESC, 1;

totalNULL විය හැකි නම් (දෙපැත්තටම හානියක් නොවනු ඇත, නමුත් පවතින දර්ශක සමඟ සැසඳීමට ඔබට අවශ්‍ය වනු ඇත ):

...
ORDER  BY customer, total DESC NULLS LAST, id;

ප්‍රධාන කරුණු

  • DISTINCT ONයනු සම්මතයේ PostgreSQL දිගුවකි (මෙහි DISTINCTසම්පූර්ණ SELECTලැයිස්තුවේ පමණක් අර්ථ දක්වා ඇත).

  • DISTINCT ONවගන්තියේ ඕනෑම ප්‍රකාශන ගණනක් ලැයිස්තුගත කරන්න , ඒකාබද්ධ පේළි අගය අනුපිටපත් අර්ථ දක්වයි. අත්පොත:

නිසැකවම, පේළි දෙකක් අවම වශයෙන් එක් තීරු අගයකින් වෙනස් නම් ඒවා එකිනෙකට වෙනස් ලෙස සැලකේ. මෙම සංසන්දනය කිරීමේදී ශුන්‍ය අගයන් සමාන යැයි සැලකේ.

නිර්භීත අවධාරණය මගේ.

  • DISTINCT ONසමඟ ඒකාබද්ධ කළ හැකිය ORDER BY. ප්‍රමුඛ ප්‍රකාශන තුළ ප්‍රකාශන ORDER BYසමූහයේ තිබිය යුතුය DISTINCT ON, නමුත් ඔබට ඒවා අතර නිදහසේ පිළිවෙල නැවත සකස් කළ හැකිය. උදාහරණයක්. එක් එක් සම වයසේ මිතුරන්ගෙන් නිශ්චිත පේළියක් තෝරා ගැනීමට ඔබට අමතර ප්‍රකාශන එකතු කළ හැකිය ORDER BY. නැතහොත්, අත්පොතෙහි දැක්වෙන පරිදි :

මෙම DISTINCT ONප්රකාශනය (ව) ෙකලවර ගැලපිය යුතුයි ORDER BY ප්රකාශනය (ව). මෙම ORDER BYවගන්තිය සාමාන්යයෙන් එක් එක් තුළ පේළි අපේක්ෂිත තැනක්ද තීරණය අමතර ප්රකාශනය (ව) අඩංගු වේ DISTINCT ONපිරිසක්.

idසබඳතා බිඳ දැමීම සඳහා මම අවසාන අයිතමය ලෙස එකතු කළෙමි :
" idසෑම කණ්ඩායමකින්ම ඉහළම දේ බෙදා ගන්නා කුඩාම අය සමඟ පේළිය තෝරන්න total."

එක් කණ්ඩායමකට පළමුවැන්න තීරණය කරන වර්ග කිරීමේ අනුපිළිවෙලට එකඟ නොවන ආකාරයෙන් ප්‍රති results ල ඇණවුම් කිරීම සඳහා, ඔබට විමසුමකට ඉහළින් විමසුමකට වඩා වෙනත් විමසුමකින් කූඩුව දැමිය හැකිය ORDER BY. උදාහරණයක්.

  • totalNULL විය හැකි නම් , ඔබට බොහෝ විට අවශ්‍ය වන්නේ විශාලතම ශුන්‍ය නොවන අගය සහිත පේළියයි. NULLS LASTනිරූපණය කළ ආකාරයට එකතු කරන්න . බලන්න:

  • ASC තීරුව අනුව වර්ග කරන්න, නමුත් මුලින් NULL අගයන්?

  • මෙම SELECTලැයිස්තුව තුල ප්රකාශන මගින් සීමා කර නැත DISTINCT ONහෝ ORDER BYකිසිම ආකාරයකින්. (ඉහත සරල අවස්ථාවෙහිදී අවශ්‍ය නොවේ):

    • ඔබ කළ යුතු නැත දී ප්රකාශන කිසිදු ඇතුළත් DISTINCT ONහෝ ORDER BY.

    • ඔබට වෙනත් ඕනෑම ප්‍රකාශනයක් ලැයිස්තුවට ඇතුළත් කළ හැකියSELECT . මෙය වඩාත් සංකීර්ණ විමසුම් උපසිරැසි සහ සමස්ත / කවුළු කාර්යයන් සමඟ ප්‍රතිස්ථාපනය කිරීමට උපකාරී වේ.

  • මම Postgres අනුවාද 8.3 - 12 සමඟ අත්හදා බැලුවෙමි. නමුත් මෙම අංගය අවම වශයෙන් 7.1 අනුවාදයේ සිට ඇත, එබැවින් මූලික වශයෙන් සෑම විටම.

දර්ශකය

මෙම පරිපූර්ණ ඉහත විමසුම සඳහා දර්ශකය විය හැකි බවට බහු-තීරු දර්ශකය ගැලපෙන අනුපිළිවෙල හා ගැලපෙන පිළිවෙල සමග සියලු තීරු තුනක් පුරාවටම:

CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);

විශේෂ specialized විය හැක. විශේෂිත විමසුම සඳහා කියවීමේ කාර්ය සාධනය ඉතා වැදගත් නම් එය භාවිතා කරන්න. ඔබට DESC NULLS LASTවිමසුමේ තිබේ නම්, දර්ශකයේ එයම භාවිතා කරන්න එවිට අනුපිළිවෙල ගැලපෙන අතර දර්ශකය අදාළ වේ.

Ective ලදායීතාවය / කාර්ය සාධනය ප්‍රශස්තකරණය

එක් එක් විමසුම සඳහා ගැලපෙන දර්ශක සෑදීමට පෙර පිරිවැය සහ ප්‍රතිලාභ කිරා මැන බලන්න. ඉහත දර්ශකයේ විභවය බොහෝ දුරට දත්ත බෙදා හැරීම මත රඳා පවතී .

දර්ශකය භාවිතා කරනුයේ එය පෙර වර්ග කළ දත්ත ලබා දෙන බැවිනි. Postgres 9.2 හෝ ඊට පසු විමසුමට දර්ශකයෙන් ප්‍රයෝජන ගත හැක්කේ දර්ශකය යටින් ඇති වගුවට වඩා කුඩා නම් පමණි . දර්ශකය සම්පූර්ණයෙන් පරිලෝකනය කළ යුතුය.

මිණුම් ලකුණ

මට මෙහි සරල මිණුම් දණ්ඩක් තිබුණි, එය මේ වන විට යල් පැන ඇත. මෙම වෙනම පිළිතුරේ සවිස්තරාත්මක මිණුම් දණ්ඩක් සමඟ මම එය ආදේශ කළෙමි .


32
බොහෝ දත්ත සමුදා ප්‍රමාණයන් සඳහා මෙය හොඳ පිළිතුරකි, නමුත් ඔබ පෙන්වා දීමට අවශ්‍ය වන්නේ ඔබ පේළි මිලියනයකට ළඟා වන විට DISTINCT ONඅතිශය මන්දගාමී වන බවයි. ක්‍රියාත්මක කිරීම සැමවිටම සමස්ත වගුව වර්ග කර අනුපිටපත් සඳහා පරිලෝකනය කරයි, සියලු දර්ශක නොසලකා හැරේ (ඔබ අවශ්‍ය බහු තීරු දර්ශකය නිර්මාණය කර තිබුණද). හැකි විසඳුමක් සඳහා පැහැදිලි කිරීම. Extended.com/2009/05/03/postgresql-optimizing-distinct බලන්න .
මීකොහි

14
"කේතය කෙටි කිරීමට" ඕඩිනල් භාවිතා කිරීම භයානක අදහසකි. එය කියවිය හැකි වන පරිදි තීරු නම් තැබීම ගැන කුමක් කිව හැකිද?
KOTJMF

13
OTKOTJMF: මම යෝජනා කරන්නේ ඔබේ පෞද්ගලික මනාපය සමඟ යන්න. මම අධ්‍යාපනය සඳහා විකල්ප දෙකම නිරූපණය කරමි. SELECTලැයිස්තුවේ දිගු ප්‍රකාශන සඳහා සින්ටැක්ස් කෙටිමං ප්‍රයෝජනවත් වේ .
අර්වින් බ්‍රෑන්ඩ්ස්ටෙටර්

1
ang ජන්ගොරෙක්කි: මුල් මිණුම් ලකුණ 2011 සිට ය, මට තවදුරටත් සැකසුම නොමැත. කෙසේ වෙතත් pg 9.4 සහ pg 9.5 සමඟ පරීක්ෂණ පැවැත්වීමට කාලය පැමිණ තිබේ. එකතු කළ පිළිතුරෙහි විස්තර බලන්න. . ඔබගේ ස්ථාපනයෙන් ප්‍රති result ල සමඟ ඔබට අදහසක් එක් කළ හැකිද?
අර්වින් බ්‍රෑන්ඩ්ස්ටෙටර්

2
Ira මුහුදු කොල්ලකරුවන්: මගේ හිස මුදුනේ සිට නොවේ. සම වයසේ මිතුරන් කණ්ඩායමකට එක් පේළියක් DISTINCT ONලබා ගැනීම සඳහා පමණක් හොඳය .
අර්වින් බ්‍රෑන්ඩ්ස්ටෙටර්

139

මිණුම් ලකුණ

පෝස්ට්ග්‍රෙස් 9.4 සහ 9.5 සමඟ වඩාත් සිත්ගන්නාසුලු අපේක්ෂකයින් පරීක්ෂා කිරීම, අඩක් යථාර්ථවාදී වගුවක් සහිත පේළි 200 ක පේළි 200 ක් purchasesසහ 10k එකිනෙකට වෙනස්customer_id ( පාරිභෝගිකයෙකුට පේළි 20 ක් ).

Postgres 9.5 සඳහා මම සුවිශේෂී ගනුදෙනුකරුවන් 86446 ක් සමඟ 2 වන පරීක්ෂණය පැවැත්වුවෙමි. පහත බලන්න ( එක් ගනුදෙනුකරුවකුට සාමාන්‍ය පේළි 2.3 ).

පිහිටුවීම

ප්‍රධාන වගුව

CREATE TABLE purchases (
  id          serial
, customer_id int  -- REFERENCES customer
, total       int  -- could be amount of money in Cent
, some_column text -- to make the row bigger, more realistic
);

මම භාවිතා කරන්නේ a serialcustomer_id වඩාත් සාමාන්‍ය සැකසුමක් බැවින් (පහත එකතු කරන ලද PK අවහිරතා) සහ පූර්ණ සංඛ්‍යාවක් . some_columnසාමාන්‍යයෙන් තවත් තීරු සෑදීම සඳහා එකතු කරන ලදි.

ව්‍යාජ දත්ත, පී.කේ., දර්ශකය - සාමාන්‍ය වගුවක මිය ගිය ටුපල් කිහිපයක් ද ඇත:

INSERT INTO purchases (customer_id, total, some_column)    -- insert 200k rows
SELECT (random() * 10000)::int             AS customer_id  -- 10k customers
     , (random() * random() * 100000)::int AS total     
     , 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int)
FROM   generate_series(1,200000) g;

ALTER TABLE purchases ADD CONSTRAINT purchases_id_pkey PRIMARY KEY (id);

DELETE FROM purchases WHERE random() > 0.9; -- some dead rows

INSERT INTO purchases (customer_id, total, some_column)
SELECT (random() * 10000)::int             AS customer_id  -- 10k customers
     , (random() * random() * 100000)::int AS total     
     , 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int)
FROM   generate_series(1,20000) g;  -- add 20k to make it ~ 200k

CREATE INDEX purchases_3c_idx ON purchases (customer_id, total DESC, id);

VACUUM ANALYZE purchases;

customer වගුව - උසස් විමසුම සඳහා

CREATE TABLE customer AS
SELECT customer_id, 'customer_' || customer_id AS customer
FROM   purchases
GROUP  BY 1
ORDER  BY 1;

ALTER TABLE customer ADD CONSTRAINT customer_customer_id_pkey PRIMARY KEY (customer_id);

VACUUM ANALYZE customer;

9.5 සඳහා මගේ දෙවන පරීක්ෂණයෙහිදී මම එකම සැකසුම භාවිතා කළෙමි, නමුත් random() * 100000උත්පාදනය සඳහාcustomer_id කිරීම සඳහා පේළි කිහිපයක් පමණක් ලබා ගැනීමට customer_id.

වගුව සඳහා වස්තු ප්රමාණ purchases

මෙම විමසුම සමඟ ජනනය කරන ලදි .

               what                | bytes/ct | bytes_pretty | bytes_per_row
-----------------------------------+----------+--------------+---------------
 core_relation_size                | 20496384 | 20 MB        |           102
 visibility_map                    |        0 | 0 bytes      |             0
 free_space_map                    |    24576 | 24 kB        |             0
 table_size_incl_toast             | 20529152 | 20 MB        |           102
 indexes_size                      | 10977280 | 10 MB        |            54
 total_size_incl_toast_and_indexes | 31506432 | 30 MB        |           157
 live_rows_in_text_representation  | 13729802 | 13 MB        |            68
 ------------------------------    |          |              |
 row_count                         |   200045 |              |
 live_tuples                       |   200045 |              |
 dead_tuples                       |    19955 |              |

විමසුම්

1. row_number()CTE හි, ( වෙනත් පිළිතුර බලන්න )

WITH cte AS (
   SELECT id, customer_id, total
        , row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn
   FROM   purchases
   )
SELECT id, customer_id, total
FROM   cte
WHERE  rn = 1;

2. row_number()උපවගන්තියේ (මගේ ප්‍රශස්තිකරණය)

SELECT id, customer_id, total
FROM   (
   SELECT id, customer_id, total
        , row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn
   FROM   purchases
   ) sub
WHERE  rn = 1;

3. DISTINCT ON( වෙනත් පිළිතුර බලන්න )

SELECT DISTINCT ON (customer_id)
       id, customer_id, total
FROM   purchases
ORDER  BY customer_id, total DESC, id;

4. උපසිරැසි සමඟ rCTE LATERAL( මෙහි බලන්න )

WITH RECURSIVE cte AS (
   (  -- parentheses required
   SELECT id, customer_id, total
   FROM   purchases
   ORDER  BY customer_id, total DESC
   LIMIT  1
   )
   UNION ALL
   SELECT u.*
   FROM   cte c
   ,      LATERAL (
      SELECT id, customer_id, total
      FROM   purchases
      WHERE  customer_id > c.customer_id  -- lateral reference
      ORDER  BY customer_id, total DESC
      LIMIT  1
      ) u
   )
SELECT id, customer_id, total
FROM   cte
ORDER  BY customer_id;

5. customerසමඟ වගුව LATERAL( මෙහි බලන්න )

SELECT l.*
FROM   customer c
,      LATERAL (
   SELECT id, customer_id, total
   FROM   purchases
   WHERE  customer_id = c.customer_id  -- lateral reference
   ORDER  BY total DESC
   LIMIT  1
   ) l;

6. array_agg()සමඟ ORDER BY( වෙනත් පිළිතුර බලන්න )

SELECT (array_agg(id ORDER BY total DESC))[1] AS id
     , customer_id
     , max(total) AS total
FROM   purchases
GROUP  BY customer_id;

ප්රතිපල

ඉහත විමසුම් සඳහා ක්‍රියාත්මක කිරීමේ කාලය EXPLAIN ANALYZE(සහ සියලු විකල්ප අක්‍රීයයි ), ලකුණු 5 න් හොඳම .

සියලුම විමසුම් මඟින් දර්ශක පමණක් පරිලෝකනය කර ඇත purchases2_3c_idx(වෙනත් පියවර අතර). ඒවායින් සමහරක් දර්ශකයේ කුඩා ප්‍රමාණයට පමණක් වන අතර අනෙක් ඒවා වඩාත් .ලදායී වේ.

A. පෝස්ට්ග්‍රෙස් 9.4 පේළි 200 ක් සහ ඩොලර් 20 බැගින් customer_id

1. 273.274 ms  
2. 194.572 ms  
3. 111.067 ms  
4.  92.922 ms  
5.  37.679 ms  -- winner
6. 189.495 ms

B. Postgres 9.5 ට සමාන වේ

1. 288.006 ms
2. 223.032 ms  
3. 107.074 ms  
4.  78.032 ms  
5.  33.944 ms  -- winner
6. 211.540 ms  

C. B. ට සමාන නමුත් පේළි 2.3 බැගින් customer_id

1. 381.573 ms
2. 311.976 ms
3. 124.074 ms  -- winner
4. 710.631 ms
5. 311.976 ms
6. 421.679 ms

අදාළ මිණුම් සලකුණු

මෙන්න සමඟ පරීක්ෂා "ogr" විසින් නව එකක් 10M පේළි හා සුවිශේෂී "පාරිභෝගිකයන්" 60k මත Postgres 11.5 (2019 සැප්තැම්බර් වන විට වත්මන්). ප්‍රති results ල තවමත් අප දැක ඇති දේට අනුකූල වේ:

2011 සිට මුල් (යල් පැන ගිය) මිණුම් ලකුණ

මම පෝස්ට්ග්‍රෙස්කියුඑල් 9.1 සමඟ පරීක්ෂණ 3 ක් සැබෑ ජීවිත වගුවක පේළි 65579 ක් හා එක් තීරු තුනක තනි තීරු බීට්‍රී දර්ශක මත ධාවනය කර ඇති අතර හොඳම ක්‍රියාත්මක කිරීමේ කාලය ලකුණු 5 කි. GOMGPonies හි පළමු විමසුම ( ) ඉහත විසඳුම
සමඟ සංසන්දනය කිරීම ( ):ADISTINCT ONB

  1. සම්පූර්ණ වගුව තෝරන්න, මෙම නඩුවේ පේළි 5958 ක් ලැබේ.

    A: 567.218 ms
    B: 386.673 ms
  2. WHERE customer BETWEEN x AND yපේළි 1000 ක් ඇති වන කොන්දේසිය භාවිතා කරන්න .

    A: 249.136 ms
    B:  55.111 ms
  3. සමඟ තනි පාරිභෝගිකයෙකු තෝරන්න WHERE customer = x.

    A:   0.143 ms
    B:   0.072 ms

අනෙක් පිළිතුරෙහි විස්තර කර ඇති දර්ශකය සමඟ එකම පරීක්ෂණය නැවත නැවතත්

CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);

1A: 277.953 ms  
1B: 193.547 ms

2A: 249.796 ms -- special index not used  
2B:  28.679 ms

3A:   0.120 ms  
3B:   0.048 ms

5
විශිෂ්ට මිණුම් දණ්ඩකට ස්තූතියි. සම්පුර්ණ වෙනුවට කාලරාමුව ඇති සිදුවීම් දත්ත විමසීමෙන් නව බ්‍රින් දර්ශකයෙන් ප්‍රයෝජන ලැබේදැයි මම කල්පනා කළෙමි. මෙය තාවකාලික විමසුම් සඳහා වේගවත් කිරීමක් ලබා දිය හැකිය.
jangorecki

3
ang ජන්ගොරෙක්කි: භෞතිකව වර්ග කළ දත්ත ඇති ඕනෑම විශාල වගුවකට බ්‍රින් දර්ශකයකින් ලාභ ලැබිය හැකිය.
අර්වින් බ්‍රෑන්ඩ්ස්ටෙටර්

RErwinBrandstetter 2. row_number()සහ 5. customer table with LATERALඋදාහරණ වලදී, හැඳුනුම්පත කුඩාම බව සහතික කරන්නේ කුමක් ද?
ආටෙම් නොවිකොව්

ආර්ටෙම්නොවිකොව්: කිසිවක් නැත. පරමාර්ථය වන්නේ customer_id ඉහළම පේළියකට ලබා ගැනීමයි total. idතෝරාගත් පේළි වල ඇති කුඩාම ඒවා බවට පත්වීම ප්‍රශ්නයේ පරීක්ෂණ දත්තවල නොමඟ යවන අහඹු සිදුවීමකි customer_id.
අර්වින් බ්‍රෑන්ඩ්ස්ටෙටර්

1
RArttemNovikov: දර්ශක පමණක් ස්කෑන් කිරීමට ඉඩ දීම.
අර්වින් බ්‍රෑන්ඩ්ස්ටෙටර්

58

මෙය පොදුයි ගැටලුව, දැනටමත් හොඳින් පරීක්‍ෂා කර ඇති සහ ඉහළ ප්‍රශස්තිකරණය කළ විසඳුම් ඇත. පුද්ගලිකව මම කැමතියි බිල් කාර්වින්ගේ වම් සම්බන්ධ වීමේ විසඳුමට ( වෙනත් විසඳුම් රාශියක් ඇති මුල් පෝස්ට් ).

මෙම පොදු ගැටලුවට විසඳුම් පොකුරක් බොහෝ නිල මූලාශ්‍රවලින් එකක් වන MySQL අත්පොතෙන් සොයාගත හැකි බව සලකන්න . පොදු විමසුම් සඳහා උදාහරණ බලන්න :: පේළි සමූහය අනුව නිශ්චිත තීරුවක උපරිමය .


24
MySQL අත්පොත Postgres / SQLite (SQL ගැන සඳහන් නොකල යුතුය) ප්‍රශ්න සඳහා “නිල” වන්නේ කෙසේද? එසේම, පැහැදිලිව DISTINCT ONකිවහොත් , අනුවාදය ස්වයං LEFT JOINහෝ අර්ධ-විරෝධී- බැඳීමක් ඇති විකල්පයන්ට වඩා කෙටි, සරල හා සාමාන්‍යයෙන් පෝස්ට්ග්‍රෙස්හි වඩා හොඳින් ක්‍රියා කරයි NOT EXISTS. එය "හොඳින් පරීක්ෂා කර ඇත".
අර්වින් බ්‍රැන්ඩ්ස්ටෙටර්

3
අර්වින් ලියූ දෙයට අමතරව, මම කියන්නේ කවුළු ශ්‍රිතයක් භාවිතා කිරීම (වර්තමානයේ එය සාමාන්‍ය SQL ක්‍රියාකාරිත්වයයි) ව්‍යුත්පන්න වගුවක් සමඟ සම්බන්ධ වීමට වඩා සෑම විටම පාහේ වේගවත් වේ
a_horse_with_no_name

6
විශිෂ්ට යොමු කිරීම්. මෙය විශාලතම කණ්ඩායම් ගැටළුව ලෙස හැඳින්වෙන බව මම දැන නොසිටියෙමි. ඔබට ස්තුතියි.
ඩේවිඩ් මෑන්

සඳහන් ප්රශ්න නොවේ උදෙසා කරන දැවැන්තම කණ්ඩායමකට n නමුත් පළමු n.
reinierpost

1
මම උත්සාහ කළ ඇණවුම්-ක්ෂේත්‍ර දෙකක, "බිල් කාර්වින්ගේ වම් එක්වීමේ විසඳුම" දුර්වල කාර්ය සාධනයක් ලබා දෙයි. මගේ අදහස පහතින් බලන්න stackoverflow.com/a/8749095/684229
ජොනී වොන්

30

Postgres හි ඔබට මේ ආකාරයට භාවිතා කළ හැකිය array_agg:

SELECT  customer,
        (array_agg(id ORDER BY total DESC))[1],
        max(total)
FROM purchases
GROUP BY customer

මෙය ඔබට ලබා දෙනු ඇත id එක් එක් පාරිභෝගිකයාගේ විශාලතම මිලදී ගැනීම ලබා දෙනු ඇත.

සැලකිල්ලට ගත යුතු කරුණු කිහිපයක්:

  • array_agg යනු සමස්ත ශ්‍රිතයකි, එබැවින් එය සමඟ ක්‍රියා කරයි GROUP BY .
  • array_aggඇණවුමක් තමාටම පමණක් නියම කිරීමට ඔබට ඉඩ දෙයි, එබැවින් එය සමස්ත විමසුමේ ව්‍යුහය සීමා නොකරයි. පෙරනිමියෙන් වෙනස් දෙයක් කිරීමට ඔබට අවශ්‍ය නම්, ඔබ NULL වර්ග කරන ආකාරය සඳහා වාක්‍ය ඛණ්ඩයක් ද ඇත.
  • අපි අරාව සෑදූ පසු, අපි පළමු අංගය ගනිමු. (Postgres අරා 1-සුචිගත කර ඇත, 0-සුචිගත කර නැත).
  • array_aggඔබගේ තෙවන ප්‍රතිදාන තීරුව සඳහා ඔබට සමාන ආකාරයකින් භාවිතා කළ හැකි නමුත් max(total)එය සරල ය.
  • මෙන් නොව DISTINCT ON, වෙනත් හේතු නිසා ඔබට එය අවශ්‍ය නම්, එය array_aggතබා ගැනීමට ඔබට ඉඩ සලසයි GROUP BY.

14

අර්වින් පෙන්වා දුන් පරිදි විසඳුම ඉතා කාර්යක්ෂම නොවේ

select * from purchases p1 where total in
(select max(total) from purchases where p1.customer=customer) order by total desc;

ස්තූතියි, ඔව් ඔබ සමඟ එකඟයි, උප සහ පිටත විමසුම අතර සම්බන්ධ වීමට ඇත්ත වශයෙන්ම වැඩි කාලයක් ගතවේ. "In" මෙහි ගැටළුවක් නොවනු ඇත, මන්ද subq ප්‍රති result ලය වනුයේ එක් පේළියක් පමණි. BTW, ඔබ පෙන්වා දෙන සින්ටැක්ස් දෝෂය කුමක්ද ??
user2407394

ඔහ් .. "ටෙරාඩාටා" සඳහා පුරුදු වී ඇත .. දැන් සංස්කරණය කර ඇත .. කෙසේ වෙතත් සෑම ගනුදෙනුකරුවෙකුටම ඉහළම එකතුවක් සොයා ගැනීමට අවශ්‍ය බැවින් මෙහි බිඳීම් අවශ්‍ය නොවේ ..
user2407394

ටයි පටියකදී එක් ගනුදෙනුකරුවෙකු සඳහා ඔබට පේළි කිහිපයක් ලැබෙන බව ඔබ දන්නවාද? එය සුදුසු ද යන්න නිශ්චිත අවශ්‍යතා මත රඳා පවතී. සාමාන්යයෙන්, එසේ නොවේ. අතෙහි ඇති ප්‍රශ්නය සඳහා, මාතෘකාව ඉතා පැහැදිලිය.
අර්වින් බ්‍රෑන්ඩ්ස්ටෙටර්

ප්‍රශ්නයෙන් මෙය පැහැදිලි නැත, එකම ගනුදෙනුකරුවකුට විවිධ අයිඩී 2 ක් සඳහා මිලදී ගැනීම = මැක්ස් තිබේ නම්, මම හිතන්නේ අපි දෙකම ප්‍රදර්ශනය කළ යුතුය.
user2407394

11

විමසුම:

SELECT purchases.*
FROM purchases
LEFT JOIN purchases as p 
ON 
  p.customer = purchases.customer 
  AND 
  purchases.total < p.total
WHERE p.total IS NULL

වැඩ කරන්නේ කෙසේද!(මම එහි සිටිමි)

එක් එක් මිලදී ගැනීම සඳහා අප සතුව ඇත්තේ ඉහළම එකතුව පමණක් බව තහවුරු කර ගැනීමට අපට අවශ්‍යය.


සමහර න්‍යායාත්මක කරුණු (ඔබට විමසුම තේරුම් ගැනීමට අවශ්‍ය නම් මෙම කොටස මඟ හරින්න)

එකතුව යනු ටී (ගනුදෙනුකරු, හැඳුනුම්පත) ශ්‍රිතයක් වන අතර එය නම සහ හැඳුනුම්පත ලබා දුන් අගයක් ලබා දෙයි. ලබා දී ඇති මුළු මුදල (ටී (පාරිභෝගික, හැඳුනුම්පත) ඉහළම බව ඔප්පු කිරීම සඳහා අප විසින් ඔප්පු කළ යුතු ඉහළම අගය

  • ∀x T (පාරිභෝගික, හැඳුනුම්පත)> ටී (පාරිභෝගික, x) (මෙම එකතුව එම පාරිභෝගිකයා සඳහා වන අනෙකුත් මුළු එකතුවට වඩා වැඩිය)

හෝ

  • ¬∃x T (පාරිභෝගික, හැඳුනුම්පත) <T (පාරිභෝගික, x) (එම පාරිභෝගිකයා සඳහා වැඩි එකතුවක් නොමැත)

පළමු ප්‍රවේශය අප සැබවින්ම අකමැති එම නම සඳහා සියලු වාර්තා ලබා ගැනීමට අපට අවශ්‍ය වනු ඇත.

දෙවැන්නාට මෙයට වඩා ඉහළ වාර්තාවක් තිබිය නොහැකි යැයි කීමට හොඳ ක්‍රමයක් අවශ්‍ය වේ.


SQL වෙත ආපසු යන්න

අප පිටව ගියහොත් නමෙහි ඇති වගුවට සම්බන්ධ වන අතර එය එකතු වූ වගුවට වඩා අඩු වේ:

      LEFT JOIN purchases as p 
      ON 
      p.customer = purchases.customer 
      AND 
      purchases.total < p.total

එකම පරිශීලකයාට සම්බන්ධ වීමට වැඩි එකතුවක් සහිත තවත් වාර්තාවක් ඇති සියලුම වාර්තා අපි සහතික කරමු:

purchases.id, purchases.customer, purchases.total, p.id, p.customer, p.total
1           , Tom           , 200             , 2   , Tom   , 300
2           , Tom           , 300
3           , Bob           , 400             , 4   , Bob   , 500
4           , Bob           , 500
5           , Alice         , 600             , 6   , Alice   , 700
6           , Alice         , 700

කණ්ඩායම් කිරීමක් අවශ්‍ය නොවන සෑම මිලදී ගැනීමක් සඳහාම ඉහළම එකතුව සඳහා පෙරීමට එය අපට උපකාරී වේ:

WHERE p.total IS NULL

purchases.id, purchases.name, purchases.total, p.id, p.name, p.total
2           , Tom           , 300
4           , Bob           , 500
6           , Alice         , 700

අපට අවශ්‍ය පිළිතුර එයයි.


10

මම මේ ආකාරයෙන් භාවිතා කරමි (postgresql පමණි): https://wiki.postgresql.org/wiki/First/last_%28aggregate%29

-- Create a function that always returns the first non-NULL item
CREATE OR REPLACE FUNCTION public.first_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$
        SELECT $1;
$$;

-- And then wrap an aggregate around it
CREATE AGGREGATE public.first (
        sfunc    = public.first_agg,
        basetype = anyelement,
        stype    = anyelement
);

-- Create a function that always returns the last non-NULL item
CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$
        SELECT $2;
$$;

-- And then wrap an aggregate around it
CREATE AGGREGATE public.last (
        sfunc    = public.last_agg,
        basetype = anyelement,
        stype    = anyelement
);

එවිට ඔබේ ආදර්ශය පාහේ ක්‍රියාත්මක විය යුතුය :

SELECT FIRST(id), customer, FIRST(total)
FROM  purchases
GROUP BY customer
ORDER BY FIRST(total) DESC;

CAVEAT: එය NULL පේළි නොසලකා හරියි


1 සංස්කරණය කරන්න - ඒ වෙනුවට postgres දිගුව භාවිතා කරන්න

දැන් මම මේ ආකාරයට භාවිතා කරමි: http://pgxn.org/dist/first_last_agg/

උබුන්ටු 14.04 හි ස්ථාපනය කිරීමට:

apt-get install postgresql-server-dev-9.3 git build-essential -y
git clone git://github.com/wulczer/first_last_agg.git
cd first_last_app
make && sudo make install
psql -c 'create extension first_last_agg'

එය ඔබට පළමු හා අවසාන කාර්යයන් ලබා දෙන පෝස්ට්ග්‍රෙස් දිගුවකි; පෙනෙන ආකාරයට ඉහත ක්‍රමයට වඩා වේගවත්.


සංස්කරණය 2 - ඇණවුම් කිරීම සහ පෙරීම

ඔබ සමස්ථ කාර්යයන් භාවිතා කරන්නේ නම් (මේ වගේ), දැනටමත් ඇණවුම් කර ඇති දත්ත අවශ්‍ය නොවී ඔබට ප්‍රති results ල ඇණවුම් කළ හැකිය:

http://www.postgresql.org/docs/current/static/sql-expressions.html#SYNTAX-AGGREGATES

එබැවින් සමාන උදාහරණය, ​​ඇණවුම් කිරීම වැනි දෙයක් වනු ඇත:

SELECT first(id order by id), customer, first(total order by id)
  FROM purchases
 GROUP BY customer
 ORDER BY first(total);

ඇත්ත වශයෙන්ම ඔබට සමස්ථයට ගැලපෙන බව පෙනෙන පරිදි ඇණවුම් කර පෙරහන් කළ හැකිය; එය ඉතා ප්‍රබල වාක්‍ය ඛණ්ඩයකි.


මෙම අභිරුචි ක්‍රියාකාරී ප්‍රවේශය ද භාවිතා කිරීම. ප්රමාණවත් විශ්ව හා සරල. දේවල් සංකීර්ණ කරන්නේ ඇයි, මෙය අනෙක් අයට වඩා සැලකිය යුතු ලෙස අඩු ක්‍රියාකාරී විසඳුමක් ද?
සර්ජි ෂචර්බකොව්

8

ඉතා වේගවත් විසඳුමක්

SELECT a.* 
FROM
    purchases a 
    JOIN ( 
        SELECT customer, min( id ) as id 
        FROM purchases 
        GROUP BY customer 
    ) b USING ( id );

හැඳුනුම්පත මඟින් වගුව සුචිගත කර ඇත්නම් ඉතා වේගයෙන්:

create index purchases_id on purchases (id);

USING වගන්තිය බොහෝ සම්මතයි. සමහර සුළු දත්ත සමුදා පද්ධති සතුව එය නොමැති බව ය.
හොල්ගර් ජාකොබ්ස්

2
විශාලතම එකතුව සමඟ ගනුදෙනුකරුවන්ගේ මිලදී ගැනීම මෙයින් සොයාගත නොහැක
ජොනී වොං


7

SQL සේවාදායකයේ ඔබට මෙය කළ හැකිය:

SELECT *
FROM (
SELECT ROW_NUMBER()
OVER(PARTITION BY customer
ORDER BY total DESC) AS StRank, *
FROM Purchases) n
WHERE StRank = 1

පැහැදිලි කිරීම: මෙහිදී සමූහය සිදු කරනු ලබන්නේ ගනුදෙනුකරුගේ පදනම මත වන අතර පසුව එය සම්පුර්ණයෙන්ම ඇණවුම් කරන්න. එවිට සෑම කණ්ඩායමකටම ස්ට්‍රෑන්ක් ලෙස අනුක්‍රමික අංකය ලබා දෙනු ලැබේ.


ඔබට ස්තුතියි! මෙය මනාව ක්‍රියාත්මක වූ අතර තේරුම් ගැනීමට සහ ක්‍රියාත්මක කිරීමට ඉතා පහසු විය.
රුහෝලා

4

PostgreSQL හි, තවත් හැකියාවක් වන්නේ first_valueකවුළු ශ්‍රිතය සමඟ ඒකාබද්ධව භාවිතා කිරීමයි SELECT DISTINCT:

select distinct customer_id,
                first_value(row(id, total)) over(partition by customer_id order by total desc, id)
from            purchases;

මම සංයුක්තයක් නිර්මාණය කළෙමි (id, total), එබැවින් අගයන් දෙකම එකම එකතුවකින් ආපසු ලබා දෙනු ලැබේ. ඔබට සෑම විටම first_value()දෙවරක් අයදුම් කළ හැකිය .


3

පිළිගත් OMG Ponies හි “ඕනෑම දත්ත සමුදායකින් සහය” විසඳුම මගේ පරීක්ෂණයෙන් හොඳ වේගයක් ඇත.

මෙන්න මම එකම ප්‍රවේශයක් ලබා දෙන නමුත් ඕනෑම දත්ත සමුදා විසඳුමක් වඩාත් සම්පූර්ණ හා පිරිසිදු කරමි. බැඳීම් සලකා බලනු ලැබේ (එක් එක් ගනුදෙනුකරුට එක් පේළියක් පමණක් ලබා ගැනීමට ඇති ආශාව උපකල්පනය කරන්න, එක් ගනුදෙනුකරුවකුට උපරිම එකතුව සඳහා බහුවිධ වාර්තා පවා), සහ වෙනත් මිලදී ගැනීමේ ක්ෂේත්‍ර (උදා: buy_payment_id) මිලදී ගැනීමේ වගුවේ සැබෑ ගැලපෙන පේළි සඳහා තෝරා ගනු ලැබේ.

ඕනෑම දත්ත සමුදායකින් සහය දක්වයි:

select * from purchase
join (
    select min(id) as id from purchase
    join (
        select customer, max(total) as total from purchase
        group by customer
    ) t1 using (customer, total)
    group by customer
) t2 using (id)
order by customer

මිලදී ගැනීමේ මේසය මත (පාරිභෝගික, මුළු) වැනි සංයුක්ත දර්ශකයක් ඇති විට මෙම විමසුම සාධාරණ ලෙස වේගවත් වේ.

සටහන:

  1. t1, t2 යනු දත්ත සමුදාය අනුව ඉවත් කළ හැකි අනුකාරක අන්වර්ථයකි.

  2. Caveat : using (...)2017 ජනවාරි මස මෙම සංස්කරණයට අනුව MS-SQL සහ Oracle db හි වගන්තියට දැනට සහය නොදක්වයි. ඔබ එය උදා: උදා on t2.id = purchase.id. දක්වා පුළුල් කළ යුතුය . USING සින්ටැක්ස් SQLite, MySQL සහ PostgreSQL වල ක්‍රියාත්මක වේ.


2

ස්නෝෆ්ලේක් / ටෙරාඩාටා කවුළු සහිත කාර්යයන් සඳහා QUALIFYක්‍රියා කරන වගන්තියට සහය දක්වයි HAVING:

SELECT id, customer, total
FROM PURCHASES
QUALIFY ROW_NUMBER() OVER(PARTITION BY p.customer ORDER BY p.total DESC) = 1

1
  • සමස්ථ පේළි සමූහයෙන් ඕනෑම (ඔබේ නිශ්චිත කොන්දේසිය අනුව) පේළියක් තෝරා ගැනීමට ඔබට අවශ්‍ය නම්.

  • ඔබට sum/avgඅමතරව තවත් ( ) එකතු කිරීමේ ශ්‍රිතයක් භාවිතා කිරීමට අවශ්‍ය නම් max/min. මේ අනුව ඔබට හෝඩුවාවක් භාවිතා කළ නොහැකDISTINCT ON

ඔබට ඊළඟ උපවගන්තිය භාවිතා කළ හැකිය:

SELECT  
    (  
       SELECT **id** FROM t2   
       WHERE id = ANY ( ARRAY_AGG( tf.id ) ) AND amount = MAX( tf.amount )   
    ) id,  
    name,   
    MAX(amount) ma,  
    SUM( ratio )  
FROM t2  tf  
GROUP BY name

amount = MAX( tf.amount )ඔබට අවශ්‍ය ඕනෑම කොන්දේසියක් එක් සීමාවකින් ප්‍රතිස්ථාපනය කළ හැකිය : මෙම උපවගන්තිය පේළි එකකට වඩා නොයැවිය යුතුය

නමුත් ඔබට එවැනි දේ කිරීමට අවශ්‍ය නම් ඔබ බොහෝ විට කවුළු කාර්යයන් සොයනු ඇත


1

SQl සේවාදායකය සඳහා වඩාත් කාර්යක්ෂම ක්‍රමය වන්නේ:

with
ids as ( --condition for split table into groups
    select i from (values (9),(12),(17),(18),(19),(20),(22),(21),(23),(10)) as v(i) 
) 
,src as ( 
    select * from yourTable where  <condition> --use this as filter for other conditions
)
,joined as (
    select tops.* from ids 
    cross apply --it`s like for each rows
    (
        select top(1) * 
        from src
        where CommodityId = ids.i 
    ) as tops
)
select * from joined

භාවිතා කළ තීරු සඳහා පොකුරු දර්ශකයක් නිර්මාණය කිරීමට අමතක නොකරන්න


0

මේ ආකාරයෙන් එය මට වැඩ කරයි:

SELECT article, dealer, price
FROM   shop s1
WHERE  price=(SELECT MAX(s2.price)
              FROM shop s2
              WHERE s1.article = s2.article
              GROUP BY s2.article)
ORDER BY article;

එක් එක් ලිපියේ ඉහළම මිල තෝරන්න

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.