तत्व संख्या के साथ PostgreSQL अनावश्यक ()


90

जब मेरे पास अलग-अलग मानों वाला एक कॉलम होता है, तो मैं unnest()फ़ंक्शन का उपयोग कर सकता हूं :

myTable
id | elements
---+------------
1  |ab,cd,efg,hi
2  |jk,lm,no,pq
3  |rstuv,wxyz

select id, unnest(string_to_array(elements, ',')) AS elem
from myTable

id | elem
---+-----
1  | ab
1  | cd
1  | efg
1  | hi
2  | jk
...

मैं तत्व संख्या कैसे शामिल कर सकता हूं? अर्थात:

id | elem | nr
---+------+---
1  | ab   | 1
1  | cd   | 2
1  | efg  | 3
1  | hi   | 4
2  | jk   | 1
...

मुझे स्रोत स्ट्रिंग में प्रत्येक तत्व की मूल स्थिति चाहिए । मैंने विंडो फ़ंक्शंस ( row_number(), rank()आदि) के साथ कोशिश की है , लेकिन मुझे हमेशा मिलता है 1। शायद इसलिए कि वे स्रोत तालिका की एक ही पंक्ति में हैं?

मुझे पता है कि यह एक खराब टेबल डिजाइन है। यह मेरा नहीं है, मैं इसे ठीक करने की कोशिश कर रहा हूं।

जवाबों:


184

9.4 या बाद के पोस्टग्रेट्स

WITH ORDINALITYसेट-रिटर्न फ़ंक्शंस के लिए उपयोग करें :

जब FROMक्लॉज में एक फ़ंक्शन द्वारा प्रत्यय लगाया जाता है WITH ORDINALITY, तो एक bigintकॉलम आउटपुट के लिए जोड़ा जाता है जो 1 से शुरू होता है और फ़ंक्शन के आउटपुट की प्रत्येक पंक्ति के लिए 1 से इंक्रीमेंट होता है। यह सेट रिटर्निंग फंक्शन्स जैसे कि के मामले में सबसे उपयोगी है unnest()

LATERALPgql -hackers पर pg 9.3l और इस थ्रेड के अनुसार फीचर के साथ संयोजन में , उपरोक्त क्वेरी अब इस प्रकार लिखी जा सकती है:

SELECT t.id, a.elem, a.nr
FROM   tbl AS t
LEFT   JOIN LATERAL unnest(string_to_array(t.elements, ','))
                    WITH ORDINALITY AS a(elem, nr) ON TRUE;

LEFT JOIN ... ON TRUEबाईं तालिका में सभी पंक्तियों को सुरक्षित रखता है, भले ही तालिका अभिव्यक्ति दाईं ओर कोई पंक्तियां न लौटाए। यदि आपको कोई चिंता नहीं है तो आप इसे अन्यथा समतुल्य, कम क्रियात्मक रूप में निहित के साथ उपयोग कर सकते हैं CROSS JOIN LATERAL:

SELECT t.id, a.elem, a.nr
FROM   tbl t, unnest(string_to_array(t.elements, ',')) WITH ORDINALITY a(elem, nr);

या सरल अगर एक वास्तविक सरणी ( arrएक सरणी स्तंभ होने के नाते) आधारित है:

SELECT t.id, a.elem, a.nr
FROM   tbl t, unnest(t.arr) WITH ORDINALITY a(elem, nr);

या न्यूनतम सिंटैक्स के साथ भी:

SELECT id, a, ordinality
FROM   tbl, unnest(arr) WITH ORDINALITY a;

aस्वचालित रूप से तालिका और स्तंभ उपनाम है। अतिरिक्त क्रमबद्धता कॉलम का डिफ़ॉल्ट नाम है ordinality। लेकिन स्पष्ट स्तंभ उपनाम और तालिका-योग्य कॉलम जोड़ने के लिए यह बेहतर (सुरक्षित, क्लीनर) है।

Postgres 8.4 - 9.3

आपके साथ row_number() OVER (PARTITION BY id ORDER BY elem)क्रमबद्ध क्रम के अनुसार संख्याएँ मिलती हैं, न कि मूल क्रमिक स्थिति की क्रमिक संख्या कि स्ट्रिंग में ।

आप बस छोड़ सकते हैं ORDER BY:

SELECT *, row_number() OVER (PARTITION by id) AS nr
FROM  (SELECT id, regexp_split_to_table(elements, ',') AS elem FROM tbl) t;

हालांकि यह सामान्य रूप से काम करता है और मैंने इसे कभी भी सरल प्रश्नों में विफल नहीं देखा, पोस्टग्रेसीएल बिना पंक्तियों के आदेश के बारे में कुछ भी नहीं कहता है ORDER BY। यह एक कार्यान्वयन विवरण के कारण काम करने के लिए होता है।

रिक्त-पृथक स्ट्रिंग में तत्वों की क्रमिक संख्या की गारंटी के लिए :

SELECT id, arr[nr] AS elem, nr
FROM  (
   SELECT *, generate_subscripts(arr, 1) AS nr
   FROM  (SELECT id, string_to_array(elements, ' ') AS arr FROM tbl) t
   ) sub;

या सरल अगर एक वास्तविक सरणी के आधार पर :

SELECT id, arr[nr] AS elem, nr
FROM  (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t;

संबंधित उत्तर dba.SE पर:

पोस्टग्रेज 8.1 - 8.4

इन सुविधाओं में से कोई भी अभी तक उपलब्ध हैं,: RETURNS TABLE, generate_subscripts(), unnest(), array_length()। लेकिन यह काम करता है:

CREATE FUNCTION f_unnest_ord(anyarray, OUT val anyelement, OUT ordinality integer)
  RETURNS SETOF record
  LANGUAGE sql IMMUTABLE AS
'SELECT $1[i], i - array_lower($1,1) + 1
 FROM   generate_series(array_lower($1,1), array_upper($1,1)) i';

विशेष रूप से ध्यान दें, कि सरणी सूचकांक तत्वों के क्रमिक पदों से भिन्न हो सकता है। एक विस्तारित फ़ंक्शन के साथ इस डेमो पर विचार करें :

CREATE FUNCTION f_unnest_ord_idx(anyarray, OUT val anyelement, OUT ordinality int, OUT idx int)
  RETURNS SETOF record
  LANGUAGE sql IMMUTABLE AS
'SELECT $1[i], i - array_lower($1,1) + 1, i
 FROM   generate_series(array_lower($1,1), array_upper($1,1)) i';

SELECT id, arr, (rec).*
FROM  (
   SELECT *, f_unnest_ord_idx(arr) AS rec
   FROM  (VALUES (1, '{a,b,c}'::text[])  --  short for: '[1:3]={a,b,c}'
               , (2, '[5:7]={a,b,c}')
               , (3, '[-9:-7]={a,b,c}')
      ) t(id, arr)
   ) sub;

 id |       arr       | val | ordinality | idx
----+-----------------+-----+------------+-----
  1 | {a,b,c}         | a   |          1 |   1
  1 | {a,b,c}         | b   |          2 |   2
  1 | {a,b,c}         | c   |          3 |   3
  2 | [5:7]={a,b,c}   | a   |          1 |   5
  2 | [5:7]={a,b,c}   | b   |          2 |   6
  2 | [5:7]={a,b,c}   | c   |          3 |   7
  3 | [-9:-7]={a,b,c} | a   |          1 |  -9
  3 | [-9:-7]={a,b,c} | b   |          2 |  -8
  3 | [-9:-7]={a,b,c} | c   |          3 |  -7

की तुलना करें:


10
यह उत्तर SO में सबसे व्यापक उत्तरों में से एक है, PostgreSQL के बारे में। धन्यवाद इरविन।
अलेक्जेंड्रोस

क्या हम नए pg संस्करणों में एक वास्तविक टेबल रिटर्न (नकली पंक्तियों नहीं) के नीचे दिए गए अनावश्यक 2 फ़ंक्शन को अनुकूलित कर सकते हैं ?
पीटर क्रूस

@ erwin-brandstetter, क्या आप इस बात पर विस्तार से चर्चा करेंगे कि / क्यों WITH ORDINALITYपसंद की जाती है generate_subscripts()? यह मुझे अच्छा लगता generate_subscripts()है क्योंकि यह सरणी में वास्तविक तत्व स्थान दिखाता है। यह उपयोगी है, उदाहरण के लिए, सरणी को अपडेट करते समय ... क्या मुझे WITH ORDINALITYइसके बजाय उपयोग करना चाहिए ?
14

1
@losthorse: मैं इसे इस तरह से रेखांकित करता हूं: SQL क्वेरी में किसी भी सेट रिटर्निंग फ़ंक्शन के WITH ORDINALITYलिए पंक्ति संख्या प्राप्त करने के लिए सामान्य समाधान है । यह सबसे तेज़, विश्वसनीय तरीका है और यह 1-डिमेंस्टेशनल, 1-आधारित सरणियों के लिए पूरी तरह से काम करने के लिए भी होता है (पोस्टग्रेज़ सरणियों के लिए डिफ़ॉल्ट, इस पर विचार करें )। यदि आप किसी अन्य प्रकार के सरणियों के साथ काम करते हैं (ज्यादातर लोग नहीं करते हैं), और आपको वास्तव में मूल ग्राहकों के साथ काम करने / काम करने की आवश्यकता है, तो जाने का रास्ता है। लेकिन शुरू करने के लिए generate_subscripts()unnest()
everytihng flattens

1
@ z0r_ मैनुअल: Table functions appearing in FROM can also be preceded by the key word LATERAL, but for functions the key word is optional; the function's arguments can contain references to columns provided by preceding FROM items in any case.
इरविन ब्रान्डसेट्टर 4

9

प्रयत्न:

select v.*, row_number() over (partition by id order by elem) rn from
(select
    id,
    unnest(string_to_array(elements, ',')) AS elem
 from myTable) v

6

सब्सक्रिप्शन जनरेटिंग फ़ंक्शंस का उपयोग करें ।
http://www.postgresql.org/docs/current/static/functions-srf.html#FUNCTIONS-SRF-SUBSCRIPTS

उदाहरण के लिए:

SELECT 
  id
  , elements[i] AS elem
  , i AS nr
FROM
  ( SELECT 
      id
      , elements
      , generate_subscripts(elements, 1) AS i
    FROM
      ( SELECT
          id
          , string_to_array(elements, ',') AS elements
        FROM
          myTable
      ) AS foo
  ) bar
;

अधिक केवल:

SELECT
  id
  , unnest(elements) AS elem
  , generate_subscripts(elements, 1) AS nr
FROM
  ( SELECT
      id
      , string_to_array(elements, ',') AS elements
    FROM
      myTable
  ) AS foo
;

3

यदि तत्व का क्रम महत्वपूर्ण नहीं है, तो आप कर सकते हैं

select 
  id, elem, row_number() over (partition by id) as nr
from (
  select
      id,
      unnest(string_to_array(elements, ',')) AS elem
  from myTable
) a

0

unnest2() व्यायाम के रूप में

Pg v8.4 से पहले के पुराने संस्करणों को उपयोगकर्ता द्वारा परिभाषित किया गया है unnest()। हम एक इंडेक्स के साथ तत्वों को वापस करने के लिए इस पुराने फ़ंक्शन को अनुकूलित कर सकते हैं:

CREATE FUNCTION unnest2(anyarray)
  RETURNS setof record  AS
$BODY$
  SELECT $1[i], i
  FROM   generate_series(array_lower($1,1),
                         array_upper($1,1)) i;
$BODY$ LANGUAGE sql IMMUTABLE;

2
यह पृष्ठ v8.4 से पहले काम नहीं करेगा, क्योंकि RETURNS TABLEअभी तक नहीं है। मैंने एक समाधान पर चर्चा करते हुए अपने उत्तर में एक अध्याय जोड़ा।
इरविन ब्रान्डेसटेटर

1
@ErwinBrandstetter, आपके उत्तर बहुत ही विचारोत्तेजक हैं, और आप 4 साल पहले (!) का एक पाठ पॉलिश कर रहे हैं ... क्या आप अपने SO ग्रंथों का उपयोग करके PostgreSQL पुस्तक लिख रहे हैं? :-)
पीटर क्रस

हाय सब, यह एक विकी है, आप संपादित कर सकते हैं (!) ... लेकिन ठीक है, मैंने सही किया setof record
पीटर क्रूस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.