একাধিক কলামে DISTINCT নির্বাচন করুন


23

মনে করুন আমাদের কাছে (a,b,c,d)একই তথ্য প্রকারের চারটি কলাম সহ একটি টেবিল রয়েছে ।

কলামগুলিতে থাকা ডেটাগুলির মধ্যে সমস্ত স্বতন্ত্র মানগুলি নির্বাচন করা এবং সেগুলি একটি একক কলাম হিসাবে ফিরিয়ে দেওয়া সম্ভব বা এটি অর্জনের জন্য আমাকে একটি ফাংশন তৈরি করতে হবে?


7
মানে SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;?
ypercubeᵀᴹ

হ্যাঁ। এটি করবে তবে আমাকে 4 টি ক্যুরি চালাতে হবে। এটি একটি পারফরম্যান্স বাধা না?
ফ্যাবরিজিও মাজনি

6
এটি একটি কোয়েরি, 4 নয়
ypercubeᵀᴹ

1
আমি কোয়েরিটি লেখার বিভিন্ন উপায় দেখতে পাচ্ছি যা উপলভ্য সূচি ইত্যাদির উপর নির্ভর করে বিভিন্ন পারফরম্যান্স থাকতে পারে, তবে আমি ভাবতে পারছি না যে কোনও ফাংশন কীভাবে সহায়তা করবে
ypercubeᵀᴹ

1
ঠিক আছে. এটি দিয়ে যেতেUNION
ফ্যাব্রিজিও মাজনি

উত্তর:


24

আপডেট: এসকিউএলফিডেলে সমস্ত 5 টি প্রশ্নের 100K সারি (এবং 2 পৃথক কেস, একটিতে কয়েকটি (25) স্বতন্ত্র মান এবং অন্যটি প্রচুর (25K মান) এর সাথে পরীক্ষিত।

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

-- Query 1. (334 ms, 368ms) 
SELECT a AS abcd FROM tablename 
UNION                           -- means UNION DISTINCT
SELECT b FROM tablename 
UNION 
SELECT c FROM tablename 
UNION 
SELECT d FROM tablename ;

অন্যটি প্রথমে UNION ALLএবং তারপরে ব্যবহার করতে হবে DISTINCT। এর জন্য আরও 4 টি টেবিল স্ক্যান প্রয়োজন হবে (এবং সূচকের কোনও ব্যবহার নেই)। মানগুলি কম হলে খারাপ দক্ষতা হয় না এবং আরও মান সহ আমার (বিস্তৃত নয়) পরীক্ষায় দ্রুততম হয়:

-- Query 2. (87 ms, 117 ms)
SELECT DISTINCT a AS abcd
FROM
  ( SELECT a FROM tablename 
    UNION ALL 
    SELECT b FROM tablename 
    UNION ALL
    SELECT c FROM tablename 
    UNION ALL
    SELECT d FROM tablename 
  ) AS x ;

অন্যান্য উত্তরগুলি অ্যারে ফাংশন বা LATERALসিনট্যাক্স ব্যবহার করে আরও বিকল্প সরবরাহ করেছে । জ্যাকের ক্যোয়ারিতে ( 187 ms, 261 ms) যুক্তিসঙ্গত পারফরম্যান্স রয়েছে তবে অ্যান্ড্রিমের ক্যোয়ারী আরও দক্ষ ( 125 ms, 155 ms) বলে মনে হচ্ছে । দুজনেই টেবিলের একটি ক্রমিক স্ক্যান করে এবং কোনও সূচক ব্যবহার করে না।

প্রকৃতপক্ষে জ্যাকের প্রশ্নের ফলাফলগুলি উপরে দেখানো থেকে কিছুটা ভাল (আমরা যদি সরিয়ে দিই order by) এবং 4 টি অভ্যন্তরীণ অপসারণ করে distinctএবং কেবল বাহ্যিকটিকে রেখে আরও উন্নত করা যায়।


অবশেষে, যদি - এবং কেবলমাত্র - 4 টি কলামের স্বতন্ত্র মান তুলনামূলকভাবে কম, আপনি WITH RECURSIVEউপরের লুজ সূচক স্ক্যান পৃষ্ঠায় বর্ণিত হ্যাক / অপটিমাইজেশন ব্যবহার করতে পারেন এবং উল্লেখযোগ্য দ্রুত ফলাফলের সাথে সমস্ত 4 সূচক ব্যবহার করতে পারেন! একই 100K সারি এবং প্রায় 25 টি স্বতন্ত্র মানগুলি 4 কলামগুলিতে ছড়িয়ে পড়ে (কেবল 2 এমএসে চলে!) যখন 25K স্বতন্ত্র মানগুলির সাথে এটি 368 এমএসের সাথে সবচেয়ে ধীর হয়:

-- Query 3.  (2 ms, 368ms)
WITH RECURSIVE 
    da AS (
       SELECT min(a) AS n  FROM observations
       UNION ALL
       SELECT (SELECT min(a) FROM observations
               WHERE  a > s.n)
       FROM   da AS s  WHERE s.n IS NOT NULL  ),
    db AS (
       SELECT min(b) AS n  FROM observations
       UNION ALL
       SELECT (SELECT min(b) FROM observations
               WHERE  b > s.n)
       FROM   db AS s  WHERE s.n IS NOT NULL  ),
   dc AS (
       SELECT min(c) AS n  FROM observations
       UNION ALL
       SELECT (SELECT min(c) FROM observations
               WHERE  c > s.n)
       FROM   dc AS s  WHERE s.n IS NOT NULL  ),
   dd AS (
       SELECT min(d) AS n  FROM observations
       UNION ALL
       SELECT (SELECT min(d) FROM observations
               WHERE  d > s.n)
       FROM   db AS s  WHERE s.n IS NOT NULL  )
SELECT n 
FROM 
( TABLE da  UNION 
  TABLE db  UNION 
  TABLE dc  UNION 
  TABLE dd
) AS x 
WHERE n IS NOT NULL ;

SQLfiddle


সংক্ষিপ্তসার হিসাবে, যখন স্বতন্ত্র মানগুলি খুব কম হয়, তখন প্রচুর মূল্যবোধের সাথে পুনরুক্তিযুক্ত কোয়েরিটি সম্পূর্ণ বিজয়ী হয়, আমার ২ য় নম্বর, জ্যাকের (নীচে উন্নত সংস্করণ) এবং অ্যান্ডরিমের অনুসন্ধানগুলি সেরা পারফরমার।


দেরী সংযোজন, 1 ম ক্যোয়ারীর একটি প্রকরণ যা অতিরিক্ত স্বতন্ত্র অপারেশন সত্ত্বেও মূল 1 ম তুলনায় আরও ভাল সম্পাদন করে এবং 2 য় এর চেয়ে সামান্য খারাপ:

-- Query 1b.  (85 ms, 149 ms)
SELECT DISTINCT a AS n FROM observations 
UNION 
SELECT DISTINCT b FROM observations 
UNION 
SELECT DISTINCT c FROM observations 
UNION 
SELECT DISTINCT d FROM observations ;

এবং জ্যাকের উন্নতি হয়েছে:

-- Query 4b.  (104 ms, 128 ms)
select distinct unnest( array_agg(a)||
                        array_agg(b)||
                        array_agg(c)||
                        array_agg(d) )
from t ;

12

আপনি এই ক্যোয়ারির মতো ল্যাটারাল ব্যবহার করতে পারেন :

SELECT DISTINCT
  x.n
FROM
  atable
  CROSS JOIN LATERAL (
    VALUES (a), (b), (c), (d)
  ) AS x (n)
;

ল্যাটারাল কীওয়ার্ডটি জোড়ার ডান দিকটি বাম দিক থেকে রেফারেন্স অবজেক্টগুলিতে অনুমতি দেয়। এই ক্ষেত্রে, ডান দিকটি একটি VALUES কনস্ট্রাক্টর যা আপনার একক কলামে রাখতে চান কলামের মানগুলির বাইরে একটি একক-কলামের উপসেট তৈরি করে। মূল ক্যোয়ারিটি কেবল নতুন কলামকে এতে উল্লেখ করে, এতে DISTINCT প্রয়োগ করে।


10

স্পষ্টতই, আমি ইপারকিউবারের পরামর্শunion মতো ব্যবহার করব তবে অ্যারে দিয়ে এটিও সম্ভব:

select distinct unnest( array_agg(distinct a)||
                        array_agg(distinct b)||
                        array_agg(distinct c)||
                        array_agg(distinct d) )
from t
order by 1;
| অদ্ভুত |
| : ----- |
| 0 |
| 1 |
| 2 |
| 3 |
| 5 |
| 6 |
| 8 |
| 9 |

এখানে ডিবিফিডল


7

সংক্ষিপ্ততম

SELECT DISTINCT n FROM observations, unnest(ARRAY[a,b,c,d]) n;

অ্যান্ড্রির ধারণাটির একটি কম ভার্বোস সংস্করণ কেবল সামান্য দীর্ঘ, তবে আরও মার্জিত এবং দ্রুত।
জন্য অনেক স্বতন্ত্র / কয়েক ডুপ্লিকেট মান:

SELECT DISTINCT n FROM observations, LATERAL (VALUES (a),(b),(c),(d)) t(n);

দ্রুততম

প্রতিটি জড়িত কলামে একটি সূচক সহ!
জন্য কয়েক স্বতন্ত্র / অনেক ডুপ্লিকেট মান:

WITH RECURSIVE
  ta AS (
   (SELECT a FROM observations ORDER BY a LIMIT 1)  -- parentheses required!
   UNION ALL
   SELECT o.a FROM ta t
    , LATERAL (SELECT a FROM observations WHERE a > t.a ORDER BY a LIMIT 1) o
   )
, tb AS (
   (SELECT b FROM observations ORDER BY b LIMIT 1)
   UNION ALL
   SELECT o.b FROM tb t
    , LATERAL (SELECT b FROM observations WHERE b > t.b ORDER BY b LIMIT 1) o
   )
, tc AS (
   (SELECT c FROM observations ORDER BY c LIMIT 1)
   UNION ALL
   SELECT o.c FROM tc t
    , LATERAL (SELECT c FROM observations WHERE c > t.c ORDER BY c LIMIT 1) o
   )
, td AS (
   (SELECT d FROM observations ORDER BY d LIMIT 1)
   UNION ALL
   SELECT o.d FROM td t
    , LATERAL (SELECT d FROM observations WHERE d > t.d ORDER BY d LIMIT 1) o
   )
SELECT a
FROM  (
       TABLE ta
 UNION TABLE tb
 UNION TABLE tc
 UNION TABLE td
 ) sub;

এটি অন্য আরসিটিই বৈকল্পিক, এটি ইতিমধ্যে পোস্ট করা একটি @ টিপিকারের মতো , তবে আমি এর ORDER BY 1 LIMIT 1পরিবর্তে ব্যবহার করি min(a)যা সাধারণত কিছুটা দ্রুত। নুল মানগুলি বাদ দেওয়ার জন্য আমার কোনও অতিরিক্ত শিকারের প্রয়োজন নেই।
এবং LATERALএকটি সম্পর্কিত সম্পর্কযুক্ত সাব-কোয়ের পরিবর্তে, কারণ এটি পরিষ্কার (প্রয়োজনীয় দ্রুত নয়)।

এই কৌশলটির উত্তর দেওয়ার ক্ষেত্রে আমার বিশদ ব্যাখ্যা:

আমি ইয়ারকিউবের এসকিউএল ফিডল আপডেট করেছি এবং আমার প্লেলিস্টে যুক্ত করেছি।


আপনি EXPLAIN (ANALYZE, TIMING OFF)সর্বোত্তম সামগ্রিক পারফরম্যান্স যাচাই করতে পরীক্ষা করতে পারেন ? (5 টি শ্রেষ্ঠ প্রভাব ক্যাশে বাদ দেওয়ার।)
এরউইন Brandstetter

মজাদার. আমি ভেবেছিলাম যে কমাতে জয়েন হওয়া প্রতিটি ক্ষেত্রে ক্রস জোনের সমতুল্য হবে, অর্থাত পারফরম্যান্সের ক্ষেত্রেও। ল্যাটারাল ব্যবহারের ক্ষেত্রে কী পার্থক্য নির্দিষ্ট?
অ্যান্ড্রি এম

অথবা আমি ভুল বুঝেছি। আপনি যখন আমার পরামর্শের কম ভার্বোস সংস্করণটি সম্পর্কে "দ্রুত" বলেছেন, আপনি কি আমার চেয়ে দ্রুত বা অযৌক্তিক সহ সিলেক্ট ডিসট্র্যাক্টের চেয়ে দ্রুততর বলতে চেয়েছিলেন?
অ্যান্ড্রি এম

1
@AndriyM: কমা হয় সমতুল্য (ছাড়া স্পষ্ট `ক্রস JOIN` সিনট্যাক্স বেঁধে শক্তিশালী যখন সমাধানে ক্রম যোগদানের)। হ্যাঁ, আমি বোঝাতে চাইছি সাথে আপনার ধারণাটি VALUES ...দ্রুত unnest(ARRAY[...])। তালিকায় LATERALসেট-রিটার্নিং ফাংশনগুলির জন্য অন্তর্ভুক্ত FROM
এরউইন ব্র্যান্ডস্টেটার

উন্নতির জন্য Thnx! আমি অর্ডার / সীমা -1 বৈকল্পিক চেষ্টা করেছিলাম কিন্তু কোনও লক্ষণীয় পার্থক্য ছিল না। ল্যাটারাল ব্যবহার করা বেশ দুর্দান্ত, একাধিক নাল চেক এড়ানো দুর্দান্ত, দুর্দান্ত। লুজ-ইনডেক্স-স্ক্যান পৃষ্ঠাতে যুক্ত করার জন্য আপনার পোস্টগ্রিসের ছেলের কাছে এই রূপটি পরামর্শ দেওয়া উচিত।
ypercubeᵀᴹ

3

আপনি পারেন, তবে আমি যেমন ফাংশনটি লিখেছি এবং পরীক্ষা করেছি তেমনি ভুল অনুভব করেছি। এটি একটি সম্পদ অপচয়।
শুধু দয়া করে একটি ইউনিয়ন ব্যবহার করুন এবং আরও নির্বাচন করুন। কেবল সুবিধা (যদি তা হয়), প্রধান টেবিল থেকে একটি একক স্ক্যান।

বর্গফুট ফ্রিডে আপনাকে বিভাজকটিকে $ থেকে অন্য কোনও কিছুতে, যেমন /

CREATE TABLE observations (
    id         serial
  , a int not null
  , b int not null
  , c int not null
  , d int not null
  , created_at timestamp
  , foo        text
);

INSERT INTO observations (a, b, c, d, created_at, foo)
SELECT (random() * 20)::int        AS a          -- few values for a,b,c,d
     , (15 + random() * 10)::int 
     , (10 + random() * 10)::int 
     , ( 5 + random() * 20)::int 
     , '2014-01-01 0:0'::timestamp 
       + interval '1s' * g         AS created_at -- ascending (probably like in real life)
     , 'aöguihaophgaduigha' || g   AS foo        -- random ballast
FROM generate_series (1, 10) g;               -- 10k rows

CREATE INDEX observations_a_idx ON observations (a);
CREATE INDEX observations_b_idx ON observations (b);
CREATE INDEX observations_c_idx ON observations (c);
CREATE INDEX observations_d_idx ON observations (d);

CREATE OR REPLACE FUNCTION fn_readuniqu()
  RETURNS SETOF text AS $$
DECLARE
    a_array     text[];
    b_array     text[];
    c_array     text[];
    d_array     text[];
    r       text;
BEGIN

    SELECT INTO a_array, b_array, c_array, d_array array_agg(a), array_agg(b), array_agg(c), array_agg(d)
    FROM observations;

    FOR r IN
        SELECT DISTINCT x
        FROM
        (
            SELECT unnest(a_array) AS x
            UNION
            SELECT unnest(b_array) AS x
            UNION
            SELECT unnest(c_array) AS x
            UNION
            SELECT unnest(d_array) AS x
        ) AS a

    LOOP
        RETURN NEXT r;
    END LOOP;

END;
$$
  LANGUAGE plpgsql STABLE
  COST 100
  ROWS 1000;

SELECT * FROM fn_readuniqu();

আপনি আসলে ঠিক যেমন কোনও ফাংশনটি এখনও ইউনিয়ন ব্যবহার করবে। প্রয়াসের জন্য যে কোনও ক্ষেত্রেই +1।
ফ্যাবরিজিও মাজনি

2
আপনি এই অ্যারে এবং কার্সার যাদুটি কেন করছেন? @ ইপারকিউবের সমাধানটি কাজ করে এবং এসকিউএল ল্যাঙ্গুয়েজ ফাংশনটিতে মোড়ানো খুব সহজ।
dezso

দুঃখিত, আমি আপনার ফাংশনটি সংকলন করতে পারিনি। আমি বোকামি কিছু করেছি। আপনি যদি এটি এখানে কাজ করার ব্যবস্থা করে থাকেন তবে দয়া করে আমাকে একটি লিঙ্ক সরবরাহ করুন এবং আমি আমার উত্তরটি ফলাফলের সাথে আপডেট করব, যাতে আমরা অন্যান্য উত্তরের সাথে তুলনা করতে পারি।
ypercubeᵀᴹ

@ টাইপারকিউব সম্পাদিত সমাধান অবশ্যই কাজ করবে। ভাবাবেগে বিভাজকটি পরিবর্তন করতে ভুলবেন না। আমি আমার স্থানীয় ডিবিতে টেবিল তৈরি করে পরীক্ষা করেছি এবং ভাল কাজ করে।
ব্যবহারকারী_0
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.