প্রতিটি গ্রুপ দ্বারা প্রথম সারিটি নির্বাচন করবেন?


1320

শিরোনামের পরামর্শ অনুসারে, আমি ক এর সাথে গোষ্ঠীযুক্ত প্রতিটি সারির প্রথম সারিটি নির্বাচন করতে চাই GROUP BY

বিশেষতঃ যদি আমার কাছে এমন একটি purchasesটেবিল থাকে যা দেখতে দেখতে লাগে:

SELECT * FROM purchases;

আমার আউটপুট:

আইডি | গ্রাহক | মোট
--- + + ---------- + + ------
 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

4
@ ফিল ২২৪৪ সর্বাধিক (মোট) জন্য অনুসন্ধান করা মোটটিকে এই সারির সংখ্যার 'আইডি' মানের সাথে সংযুক্ত করবে না।
বিশ্বব্যাপী

উত্তর:


1113

ওরাকল 9.2+ এ (মূলত বর্ণিত 8i + নয়), এসকিউএল সার্ভার 2005+, পোস্টগ্র্যাস এসকিউএল 8.4+, ডিবি 2, ফায়ারবার্ড 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 উইন্ডো ফাংশনগুলিও সমর্থন করে (সিটিই যদিও উত্পন্ন টেবিলে রূপান্তরিত হওয়া প্রয়োজন)। এবং ফায়ারবার্ড 3.0 উইন্ডো ফাংশনগুলিও সমর্থন করবে
a_horse_with_no_name

37
ROW_NUMBER() OVER(PARTITION BY [...])কিছু অন্যান্য অপ্টিমাইজেশনের সাহায্যে আমাকে 30 সেকেন্ড থেকে কয়েক মিলি সেকেন্ডে একটি কোয়েরি পেতে সহায়তা করেছে। ধন্যবাদ! (পোস্টগ্র্যাসকিউএল 9.2)
স্যাম

8
যদি কোনও totalগ্রাহকের জন্য সমানভাবে সর্বোচ্চের সাথে একাধিক ক্রয় হয় তবে 1 ম ক্যোয়ারী একটি স্বেচ্ছাসেবী বিজয়ীকে ফেরত দেয় (বাস্তবায়নের বিশদগুলির উপর নির্ভর করে; idপ্রতিটি সম্পাদনের জন্য পরিবর্তন করতে পারে!)। সাধারণত (সর্বদা নয়) আপনি গ্রাহক প্রতি এক সারি চাইবেন , "ছোটদের সাথে একটি" এর মতো অতিরিক্ত মানদণ্ড দ্বারা সংজ্ঞায়িত id। সমাধানের জন্য, পরিশেষে যোগ idকরার ORDER BYতালিকা row_number()। তারপরে আপনি ২ য় ক্যোয়ারীর মতো একই ফলাফল পাবেন যা এই ক্ষেত্রে খুব কার্যকর । এছাড়াও, প্রতিটি অতিরিক্ত কলামের জন্য আপনার আরও একটি সাবকিউয়ের প্রয়োজন হবে।
এরউইন ব্র্যান্ডসেটেটার

2
গুগলের বিগকোওয়ারি প্রথম ক্যোয়ারির ROW_NUMBER () কমান্ডকে সমর্থন করে। আমাদের জন্য কবজির মতো কাজ করেছেন
প্রেক্সাইটেলস

2
নোট করুন যে উইন্ডো ফাংশন সহ প্রথম সংস্করণটি এসকিউএল সংস্করণ 3.25.0 হিসাবে কাজ করে: sqlite.org/windowfunitions.html#history
bianz

1146

ইন পোস্টগ্রি এই সাধারণত হয় সহজ এবং দ্রুততর (নীচের বেশি কর্মক্ষমতা অপ্টিমাইজেশান):

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;

যদি totalনুল হতে পারে (কোনওভাবেই আঘাত করবে না, তবে আপনি বিদ্যমান সূচিগুলি মেলাতে চাইবেন ):

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

প্রধান পয়েন্ট

  • DISTINCT ONস্ট্যান্ডার্ডের একটি পোস্টগ্রিজ এসকিউএল এক্সটেনশন (যেখানে কেবলমাত্র 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" "

    ফলাফলগুলি এমনভাবে অর্ডার করতে যাতে প্রতি গ্রুপে প্রথমটি নির্ধারণ করা বাছাই করা আদেশের সাথে একমত নয়, আপনি অন্যের সাথে বাইরের ক্যোয়ারিতে উপরে কোয়েরি করতে পারেন ORDER BYউদাহরণ।

  • যদি totalশূন্য হতে পারে, আপনি সম্ভবত সর্বশ্রেষ্ঠ অ নাল মান সারি চাই। যোগ NULLS LASTপ্রদর্শিত মত। দেখা:

  • SELECTতালিকা এক্সপ্রেশন দ্বারা সীমাবদ্ধ নয় DISTINCT ONবা ORDER BYঅন্য কোন উপায়ে। (উপরের সাধারণ ক্ষেত্রে প্রয়োজন নেই):

    • আপনি করতে হবে না এক্সপ্রেশন কোন অন্তর্ভুক্ত DISTINCT ONবা ORDER BY

    • আপনি তালিকায় অন্য কোনও অভিব্যক্তি অন্তর্ভুক্ত করতে পারেনSELECT । সাবকিউরিস এবং সমষ্টি / উইন্ডো ফাংশনগুলির সাথে আরও জটিল জটিল প্রশ্নের প্রতিস্থাপনের জন্য এটি সহায়ক।

  • আমি পোস্টগ্রিসের 8.3 - 12 সংস্করণ দিয়ে পরীক্ষা করেছি তবে বৈশিষ্টটি কমপক্ষে 7.1 সংস্করণ থেকেই রয়েছে, তাই মূলত সর্বদা।

সূচক

নিখুঁত উপরে ক্যোয়ারীর জন্য সূচক একটি হবে মাল্টি-কলাম সূচক ক্রম মিলে এবং মেলা সাজানোর ক্রম সঙ্গে তিনটি কলাম spanning:

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

খুব বিশেষজ্ঞ হতে পারে। তবে নির্দিষ্ট ক্যোয়ারির পড়ার পারফরম্যান্স গুরুত্বপূর্ণ হলে এটি ব্যবহার করুন। আপনার যদি DESC NULLS LASTক্যোয়ারীতে থাকে, তবে সূচকে একই ব্যবহার করুন যাতে ক্রমের ক্রম মেল যায় এবং সূচক প্রযোজ্য।

কার্যকারিতা / পারফরম্যান্স অপ্টিমাইজেশন

প্রতিটি ক্যোয়ারির জন্য উপযুক্ত সূচী তৈরির আগে ব্যয় ও বেনিফিট ওজন করুন। উপরের সূচকের সম্ভাবনা মূলত ডেটা বিতরণের উপর নির্ভর করে ।

সূচিটি ব্যবহৃত হয় কারণ এটি প্রাক সাজানো ডেটা সরবরাহ করে। পোস্টগ্রিস ৯.২ বা তারপরে কোয়েরিতে সূচি অন্তর্ভুক্ত টেবিলের চেয়ে ছোট হলে কেবল সূচক স্ক্যান থেকেও উপকার পাওয়া যাবে । সূচকটি পুরোপুরি স্ক্যান করতে হবে, যদিও।

মাপকাঠি

আমার এখানে একটি সাধারণ বেঞ্চমার্ক ছিল যা এখনই পুরানো। আমি পৃথক পৃথক উত্তরে এটি একটি বিশদ মাপদণ্ড দিয়ে প্রতিস্থাপন করেছি ।


28
এটি বেশিরভাগ ডাটাবেস আকারের জন্য একটি দুর্দান্ত উত্তর, তবে আমি এটি উল্লেখ করতে চাই যে আপনি যখন মিলিয়ন ডলারে পৌঁছেছেন তখন DISTINCT ONঅত্যন্ত ধীর হয়ে যায়। বাস্তবায়ন সর্বদা পুরো টেবিলটি সাজায় এবং অনুলিপিগুলির মাধ্যমে এটির মাধ্যমে স্ক্যান করে সমস্ত সূচক উপেক্ষা করে (এমনকি আপনি যদি প্রয়োজনীয় মাল্টি-কলাম-ইনডেক্স তৈরি করে থাকেন)। একটি সম্ভাব্য সমাধানের জন্য ব্যাখ্যাটি দেখুন / 2009/ 05/ 03/ postgresql- optimizing- distinct।
মেকোহি

14
"কোডটি সংক্ষিপ্ত করতে" অর্ডিনালগুলি ব্যবহার করা একটি ভয়ানক ধারণা। কীভাবে কলামের নামগুলি পড়ে পঠনযোগ্য করে রাখা যায়?
KOTJMF

13
@ কোটজেএমএফ: আমি পরামর্শ দিই আপনি তখন নিজের ব্যক্তিগত পছন্দ নিয়ে যান। আমি শিক্ষিত উভয় বিকল্প প্রদর্শন। সিনট্যাক্স শর্টহ্যান্ড SELECTতালিকার দীর্ঘ প্রকাশের জন্য দরকারী হতে পারে ।
এরউইন ব্র্যান্ডস্টেটার

1
@ জাঙ্গোরেকি: আসল বেঞ্চমার্কটি ২০১১ সালের, আমার আর কোনও সেটআপ নেই। তবে যাইহোক pg 9.4 এবং pg 9.5 দিয়ে পরীক্ষা চালানোর সময় হয়েছিল। যুক্ত উত্তরে বিশদটি দেখুন। । আপনি নীচে আপনার ইনস্টলেশন ফলাফল সঙ্গে একটি মন্তব্য যোগ করতে পারেন?
এরউইন ব্র্যান্ডসেটেটার

2
@ পাইরেট অ্যাপ: আমার মাথার উপর থেকে নয়। পিয়ার্সের প্রতি গ্রুপে এক সারি DISTINCT ONপাওয়ার জন্য এটি কেবল ভাল ।
এরউইন ব্র্যান্ডসটেটার

134

মাপকাঠি

Postgres সঙ্গে সবচেয়ে আকর্ষণীয় প্রার্থীদের পরীক্ষা করা 9.4 এবং 9.5 একটি অর্ধেক বাস্তবসম্মত টেবিলের সাথে 200K সারি মধ্যে purchasesএবং 10k স্বতন্ত্রcustomer_id ( গড়। গ্রাহক প্রতি 20 সারি )।

পোস্টগ্রিস 9.5 এর জন্য আমি কার্যকরভাবে 86446 স্বতন্ত্র গ্রাহকদের সাথে একটি দ্বিতীয় পরীক্ষা চালিয়েছি। নীচে দেখুন ( প্রতি গ্রাহকের গড় 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
);

আমি একটি serial(নীচে যুক্ত পিকে সীমাবদ্ধ) এবং একটি পূর্ণসংখ্যা ব্যবহার করি customer_idকারণ এটি আরও সাধারণ সেটআপ। 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_idcustomer_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()সিটিইতে, ( অন্যান্য উত্তর দেখুন )

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;

২. 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;

৩. DISTINCT ON( অন্যান্য উত্তর দেখুন )

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

৪. সাবটিয়ের সাথে 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;

With. 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(অন্যান্য পদক্ষেপের মধ্যে) among এর মধ্যে কিছু কেবল সূচকের ছোট আকারের জন্য, অন্যরা আরও কার্যকরভাবে।

A. 200k সারি এবং প্রতি 20 ডলার দিয়ে 9.4 পোস্ট করে 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

বি। পোস্টগ্রিস 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  

সি। বি হিসাবে একই, তবে প্রতি ~ 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

সম্পর্কিত মানদণ্ড

পোস্টগ্রেস 11.5 (সেপ্টেম্বর, 2019- তে বর্তমান) তে 10 এম সারি এবং 60 কে অনন্য "গ্রাহক" দিয়ে "ওজিআর" পরীক্ষার মাধ্যমে এখানে একটি নতুন দেওয়া হয়েছে । ফলাফল আমরা এখনও পর্যন্ত যা দেখেছি তার সাথে সামঞ্জস্যপূর্ণ:

আসল (পুরানো) বেনমার্ক ২০১১ থেকে

আমি পোস্টগ্র্রেএসকিউএল 9.1 এর সাথে 65579 সারিগুলির একটি রিয়েল লাইফ টেবিল এবং তিনটি কলামের প্রত্যেকটিতে সিঙ্গল-কলাম বিটি্রি ইনডেক্সের সাথে তিনটি পরীক্ষা চালিয়েছি এবং 5 টির সেরা মৃত্যুদন্ড কার্যকর সময় নিয়েছি । @ ওএমজিপনিজের প্রথম ক্যোয়ারী ( ) উপরের সমাধানের সাথে
তুলনা করা ( ):ADISTINCT ONB

  1. পুরো টেবিলটি নির্বাচন করুন, এক্ষেত্রে 5958 টি সারিতে ফলাফল।

    A: 567.218 ms
    B: 386.673 ms
  2. WHERE customer BETWEEN x AND y1000 সারি ফলে শর্ত ব্যবহার করুন ।

    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
@ জাঙ্গোরেকি: শারীরিকভাবে সাজানো ডেটা সহ যে কোনও বিশাল টেবিল একটি ব্রিন সূচক থেকে লাভ করতে পারে।
এরউইন ব্র্যান্ডসেটেটার

@ এরউইন ব্র্যান্ডসটেটার 2. row_number()এবং 5. customer table with LATERALউদাহরণগুলিতে, আইডিটি সবচেয়ে ছোট হবে তা কী নিশ্চিত করে?
আর্টেম নোভিকভ

@ আর্টেমনোভিকভ: কিছুই নেই। customer_id সর্বাধিক সহ সারি প্রতি উদ্দেশ্যটি পুনরুদ্ধার করা totalidনির্বাচিত সারিগুলির মধ্যে প্রতিটির চেয়ে ছোট হওয়া প্রশ্নটির পরীক্ষার তথ্যগুলিতে এটি একটি বিভ্রান্তিকর কাকতালীয় ঘটনা customer_id
এরউইন ব্র্যান্ডসেটেটার

1
@ আর্টেমনোভিকভ: কেবলমাত্র সূচি-স্ক্যানের অনুমতি দেওয়ার জন্য।
এরউইন ব্র্যান্ডসটেটার

55

এটি সাধারণ সমস্যা, যা ইতিমধ্যে ভাল পরীক্ষা এবং অত্যন্ত অনুকূলিতকরণ সমাধান আছে । ব্যক্তিগতভাবে আমি বিল কারভিনের ( অন্যান্য সমাধানের সাথে মূল পোস্ট ) বাম সংযুক্তির সমাধানটি পছন্দ করি ।

নোট করুন যে এই সাধারণ সমস্যার সমাধানের গুচ্ছটি আশ্চর্যজনকভাবে বেশিরভাগ সরকারী উত্স মাইএসকিউএল ম্যানুয়ালটিতে পাওয়া যাবে ! সাধারণ প্রশ্নের উদাহরণগুলি দেখুন: সুনির্দিষ্টভাবে নির্দিষ্ট কলামের গোষ্ঠীভিত্তিক সর্বাধিক ধারন করা রয়েছে


22
মাইএসকিউএল ম্যানুয়াল কীভাবে কোনওভাবে পোস্টগ্র্রেস / এসকিউএলাইট (এসকিউএল এর উল্লেখ না করে) প্রশ্নের জন্য "অফিসিয়াল"? এছাড়াও, স্পষ্ট করে বলতে গেলে, DISTINCT ONসংস্করণটি অনেক খাটো, সরল এবং সাধারণত স্ব- LEFT JOINবা আধা-অ্যান্টি- জয়েন্টের বিকল্পগুলির চেয়ে পোস্টগ্র্রেসে আরও ভাল সম্পাদন করে NOT EXISTS। এটি "ভাল পরীক্ষা করা "ও হয়।
এরউইন ব্র্যান্ডসটেটার

3
এরউইন যা লিখেছেন তা ছাড়াও, আমি বলতে পারি যে একটি উইন্ডো ফাংশন (যা আজকাল সাধারণ এসকিউএল কার্যকারিতা) ব্যবহার করা প্রায়শই সবসময় দ্রুত প্রাপ্ত টেবিলের সাথে যুক্ত ব্যবহারের চেয়ে দ্রুত
a_horse_with_no_name

6
দুর্দান্ত রেফারেন্স। আমি জানতাম না যে এটিকে গ্রুপ হিসাবে সবচেয়ে বড়-এন-বলা হয়। ধন্যবাদ.
ডেভিড মান

প্রশ্ন করে না হিসাবে সর্বশ্রেষ্ঠ গ্রুপ প্রতি এন কিন্তু প্রথম এন।
পুনরায় পোস্টার

1
দুটি অর্ডার-ফিল্ডের ক্ষেত্রে আমি চেষ্টা করেছিলাম, "বিল কারভিনের বামে যোগদানের সমাধান" খারাপ পারফরম্যান্স দেয়। আমার মন্তব্য নীচে দেখুন stackoverflow.com/a/8749095/684229
জনি ওয়াং

30

পোস্টগ্রিসে আপনি এটি ব্যবহার করতে পারেন 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 বাছাই করবেন তার জন্য সিনট্যাক্সও রয়েছে, যদি আপনাকে ডিফল্ট থেকে আলাদা কিছু করার প্রয়োজন হয়।
  • একবার অ্যারে তৈরি করার পরে আমরা প্রথম উপাদানটি নিই। (পোস্টগ্রিস অ্যারেগুলি 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;

ধন্যবাদ, হ্যাঁ আপনার সাথে একমত, সাবক এবং বাইরের ক্যোয়ারির মধ্যে যোগদান আসলে বেশি সময় নেয়। "ইন" এখানে কোনও সমস্যা হবে না কারণ সাবকিউ কেবলমাত্র একটি সারি তৈরি করবে। বিটিডাব্লু, আপনি কোন সিনট্যাক্স ত্রুটিটি নির্দেশ করছেন ??
ব্যবহারকারী2407394

ওহ .. "তেরদাটা" ব্যবহার করত .. এখনই সম্পাদিত..তখন প্রতিটি গ্রাহকের জন্য সর্বোচ্চ মোট সন্ধান করার জন্য এখানে ব্রেকিং সম্পর্ক প্রয়োজন হয় না ..
ব্যবহারকার 404099

আপনি সচেতন যে টাই করার ক্ষেত্রে আপনি একক গ্রাহকের জন্য একাধিক সারি পেয়েছেন? এটি পছন্দসই কিনা তা সঠিক প্রয়োজনীয়তার উপর নির্ভর করে। সাধারণত, তা হয় না। হাতে থাকা প্রশ্নের জন্য, শিরোনামটি বেশ পরিষ্কার।
এরউইন ব্র্যান্ডসেটেটার

এটি প্রশ্ন থেকে পরিষ্কার নয়, যদি একই গ্রাহকের 2 টি আলাদা আইডির জন্য সর্বোচ্চ = সর্বোচ্চ 2 টি থাকে তবে আমি মনে করি আমাদের উভয়টি প্রদর্শন করা উচিত।
ব্যবহারকারী2407394

10

আমি এইভাবে ব্যবহার করি (কেবল পোস্টগ্রেস্কল): 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;

কায়েট: এটি নালার সারিগুলিকে উপেক্ষা করে


সম্পাদনা 1 - পরিবর্তে পোস্টগ্রস এক্সটেনশন ব্যবহার করুন

এখন আমি এইভাবে ব্যবহার করি: 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 - ক্রম এবং ফিল্টারিং

আপনি যদি সামগ্রিক ফাংশনগুলি ব্যবহার করেন (এর মতো), আপনি ইতিমধ্যে ডেটা অর্ডার না করেই ফলাফলগুলি অর্ডার করতে পারেন:

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);

আপনি সামগ্রিকের মধ্যে উপযুক্ত হিসাবে বিবেচিত হিসাবে আপনি অর্ডার এবং ফিল্টার করতে পারেন; এটি খুব শক্তিশালী বাক্য গঠন।


পাশাপাশি এই কাস্টম ফাংশন পদ্ধতির ব্যবহার। পর্যাপ্তভাবে সর্বজনীন এবং সহজ। জিনিসগুলিকে কেন জটিল করুন, এটি কি অন্যদের তুলনায় উল্লেখযোগ্যভাবে কম পারফরম্যান্ট সমাধান?
সের্গে শেচারবাকভ

9

প্রশ্ন:

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

ওটা কিভাবে কাজ করে! (আমি সেখানে ছিলাম)

আমরা নিশ্চিত করতে চাই যে প্রতিটি ক্রয়ের জন্য আমাদের কাছে সর্বোচ্চ মোট রয়েছে।


কিছু তাত্ত্বিক স্টাফ (যদি আপনি কেবল ক্যোরিটি বুঝতে চান তবে এই অংশটি এড়িয়ে যান)

টোটালটিকে একটি ফাংশন টি (গ্রাহক, আইডি) হতে দিন যেখানে এটি নাম এবং আইডির প্রদত্ত একটি মূল্য ফেরত দেয় যা প্রমাণ করে যে প্রদত্ত মোট (টি (গ্রাহক, আইডি)) আমাদের সর্বোচ্চ প্রমাণ করতে হবে যে আমরা কোনওটি প্রমাণ করতে চাই

  • Tx টি (গ্রাহক, আইডি)> টি (গ্রাহক, এক্স) (এই মোটটি সেই গ্রাহকের জন্য অন্যান্য সমস্ত তুলনায় বেশি)

অথবা

  • Tx টি (গ্রাহক, আইডি) <টি (গ্রাহক, এক্স) (সেই গ্রাহকের জন্য উচ্চতর মোট কোনও উপস্থিত নেই)

প্রথম পদ্ধতির জন্য আমাদের সেই নামটির সমস্ত রেকর্ডগুলি পাওয়া দরকার যা আমি সত্যিই পছন্দ করি না।

এটির চেয়ে বড় কোনও রেকর্ড আর থাকতে পারে না তা বলার জন্য দ্বিতীয়টির একটি স্মার্ট উপায় প্রয়োজন।


এসকিউএল ফিরে যান

যদি আমরা নামটিতে টেবিলের সাথে যোগ দেয় এবং যোগ হওয়া টেবিলের চেয়ে মোট কম হয়:

      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

এবং এটি আমাদের প্রয়োজন উত্তর।


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);

ইউএসিং ক্লজটি অনেক বেশি স্ট্যান্ডার্ড। এটি কেবলমাত্র কিছু ছোট ডাটাবেস সিস্টেমে নেই।
হলগার জ্যাকবস

2
এটি বৃহত্তর মোটের সাথে গ্রাহকদের ক্রয় খুঁজে
জনি ওয়াং

7

এসকিউএল সার্ভারে আপনি এটি করতে পারেন:

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

Explaination: এখানে গোষ্ঠী গ্রাহকের ভিত্তিতে সম্পন্ন করা হয় এবং তারপর StRank যেমন মোট তারপর প্রতিটি যেমন গ্রুপ দেওয়া হয় ক্রমিক সংখ্যা দ্বারা এটি অর্ডার এবং আমরা প্রথম 1 গ্রাহক যার 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

গৃহীত ওএমজি পনিসের "যে কোনও ডাটাবেস দ্বারা সমর্থিত" সমাধানটির আমার পরীক্ষার থেকে ভাল গতি রয়েছে।

এখানে আমি একই দৃষ্টিভঙ্গি সরবরাহ করি, তবে যে কোনও ডাটাবেস সমাধান আরও সম্পূর্ণ এবং পরিষ্কার করি। বন্ধনগুলি বিবেচনা করা হয় (প্রতিটি গ্রাহকের জন্য কেবলমাত্র এক সারি, এমনকি প্রতিটি গ্রাহককে সর্বোচ্চ মোটের জন্য একাধিক রেকর্ড পাওয়ার আকাঙ্ক্ষা অনুমান করুন) এবং অন্যান্য ক্রয় ক্ষেত্রগুলি (যেমন: ক্রয়_পামত_আইডি) ক্রয় সারণীতে প্রকৃত মিলের সারিগুলির জন্য নির্বাচন করা হবে।

যে কোনও ডাটাবেস দ্বারা সমর্থিত:

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. ক্যাভেট : এই using (...)ধারাটি বর্তমানে জানুয়ারী 2017 তে এই সম্পাদনা হিসাবে এমএস-এসকিউএল এবং ওরাকল ডিবিতে সমর্থিত নয় You আপনাকে নিজেকে এটিকে উদাহরণস্বরূপ প্রসারিত করতে হবে on t2.id = purchase.idetc. ইত্যাদি সিনট্যাক্স এসকিউএলাইট, মাইএসকিউএল এবং পোস্টগ্রিসকিউএল এ কাজ করে।


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

এসকিউএল সার্ভারের জন্য সবচেয়ে কার্যকর উপায় হ'ল:

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

এবং ব্যবহৃত কলামগুলির জন্য ক্লাস্টারড সূচক তৈরি করতে ভুলবেন না

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.