PostgreSQL
 sql >> डेटाबेस >  >> RDS >> PostgreSQL

विशिष्ट संख्या में संघों के साथ एक पंक्ति खोजने के लिए SQL क्वेरी

यह का मामला है - अतिरिक्त विशेष आवश्यकता के साथ कि एक ही बातचीत में कोई अतिरिक्त नहीं होगा उपयोगकर्ता।

मानते हैं तालिका का पीके है "conversationUsers" जो संयोजनों की विशिष्टता को लागू करता है, NOT NULL और परोक्ष रूप से प्रदर्शन के लिए आवश्यक सूचकांक भी प्रदान करता है। इस . में बहुस्तंभ PK के स्तंभ गण! नहीं तो आपको और अधिक करना होगा।
इंडेक्स कॉलम के क्रम के बारे में:

मूल प्रश्न के लिए, "जानवर बल" . है सभी . के लिए मेल खाने वाले उपयोगकर्ताओं की संख्या गिनने के लिए दृष्टिकोण सभी दिए गए उपयोगकर्ताओं की बातचीत और फिर सभी दिए गए उपयोगकर्ताओं से मेल खाने वाले को फ़िल्टर करें। छोटी तालिकाओं और/या केवल छोटी इनपुट सरणी और/या प्रति उपयोगकर्ता कुछ बातचीत के लिए ठीक है, लेकिन अच्छी तरह से स्केल नहीं करता :

SELECT "conversationId"
FROM   "conversationUsers" c
WHERE  "userId" = ANY ('{1,4,6}'::int[])
GROUP  BY 1
HAVING count(*) = array_length('{1,4,6}'::int[], 1)
AND    NOT EXISTS (
   SELECT FROM "conversationUsers"
   WHERE  "conversationId" = c."conversationId"
   AND    "userId" <> ALL('{1,4,6}'::int[])
   );

NOT EXISTS . के साथ अतिरिक्त उपयोगकर्ताओं के साथ बातचीत को समाप्त करना विरोधी अर्ध-जुड़ना। अधिक:

वैकल्पिक तकनीकें:

कई अन्य हैं, (बहुत) तेज़ क्वेरी तकनीक। लेकिन सबसे तेज़ वाले गतिशील . के लिए उपयुक्त नहीं हैं उपयोगकर्ता आईडी की संख्या।

तेज़ क्वेरी . के लिए जो उपयोगकर्ता आईडी की एक गतिशील संख्या से भी निपट सकता है, एक पर विचार करें। पुनरावर्ती सीटीई :

WITH RECURSIVE rcte AS (
   SELECT "conversationId", 1 AS idx
   FROM   "conversationUsers"
   WHERE  "userId" = ('{1,4,6}'::int[])[1]

   UNION ALL
   SELECT c."conversationId", r.idx + 1
   FROM   rcte                r
   JOIN   "conversationUsers" c USING ("conversationId")
   WHERE  c."userId" = ('{1,4,6}'::int[])[idx + 1]
   )
SELECT "conversationId"
FROM   rcte r
WHERE  idx = array_length(('{1,4,6}'::int[]), 1)
AND    NOT EXISTS (
   SELECT FROM "conversationUsers"
   WHERE  "conversationId" = r."conversationId"
   AND    "userId" <> ALL('{1,4,6}'::int[])
   );

उपयोग में आसानी के लिए इसे किसी फ़ंक्शन या तैयार स्टेटमेंट में लपेटें . पसंद:

PREPARE conversations(int[]) AS
WITH RECURSIVE rcte AS (
   SELECT "conversationId", 1 AS idx
   FROM   "conversationUsers"
   WHERE  "userId" = $1[1]

   UNION ALL
   SELECT c."conversationId", r.idx + 1
   FROM   rcte                r
   JOIN   "conversationUsers" c USING ("conversationId")
   WHERE  c."userId" = $1[idx + 1]
   )
SELECT "conversationId"
FROM   rcte r
WHERE  idx = array_length($1, 1)
AND    NOT EXISTS (
   SELECT FROM "conversationUsers"
   WHERE  "conversationId" = r."conversationId"
   AND    "userId" <> ALL($1);

कॉल करें:

EXECUTE conversations('{1,4,6}');

db<>fiddle यहां (एक फ़ंक्शन भी दिखा रहा है )

अभी भी सुधार की गुंजाइश है:शीर्ष पाने के लिए प्रदर्शन आपको कम से कम बातचीत वाले उपयोगकर्ताओं को अपने इनपुट ऐरे में पहले रखना होगा ताकि जितनी जल्दी संभव हो उतनी पंक्तियों को समाप्त किया जा सके। शीर्ष प्रदर्शन प्राप्त करने के लिए आप गतिशील रूप से एक गैर-गतिशील, गैर-पुनरावर्ती क्वेरी उत्पन्न कर सकते हैं (तेज़ में से किसी एक का उपयोग करके) पहले लिंक से तकनीक) और बदले में उस पर अमल करें। आप इसे डायनेमिक SQL के साथ एकल plpgsql फ़ंक्शन में भी लपेट सकते हैं ...

अधिक स्पष्टीकरण:

वैकल्पिक:कम लिखी गई तालिका के लिए एमवी

यदि तालिका "conversationUsers" अधिकतर केवल पढ़ने के लिए है (पुरानी बातचीत के बदलने की संभावना नहीं है) आप का उपयोग कर सकते हैं। MATERIALIZED VIEW पूर्व-एकत्रित उपयोगकर्ताओं के साथ क्रमबद्ध सरणियों में और उस सरणी स्तंभ पर एक सादा btree अनुक्रमणिका बनाएँ।

CREATE MATERIALIZED VIEW mv_conversation_users AS
SELECT "conversationId", array_agg("userId") AS users  -- sorted array
FROM (
   SELECT "conversationId", "userId"
   FROM   "conversationUsers"
   ORDER  BY 1, 2
   ) sub
GROUP  BY 1
ORDER  BY 1;

CREATE INDEX ON mv_conversation_users (users) INCLUDE ("conversationId");

प्रदर्शित कवरिंग इंडेक्स के लिए Postgres 11 की आवश्यकता होती है। देखें:

सबक्वेरी में पंक्तियों को छाँटने के बारे में:

पुराने संस्करणों में (users, "conversationId") पर एक सादे बहु-स्तंभ अनुक्रमणिका का उपयोग करें . बहुत लंबी सरणियों के साथ, हैश इंडेक्स 10 या बाद के पोस्टग्रेज में सार्थक हो सकता है।

तब अधिक तेज़ क्वेरी बस होगी:

SELECT "conversationId"
FROM   mv_conversation_users c
WHERE  users = '{1,4,6}'::int[];  -- sorted array!

db<>fiddle यहां

प्रदर्शन पढ़ने के लाभों के विरुद्ध आपको भंडारण, लेखन और रखरखाव में अतिरिक्त लागतों को तौलना होगा।

इसके अलावा:दोहरे उद्धरण चिह्नों के बिना कानूनी पहचानकर्ताओं पर विचार करें। conversation_id "conversationId" . के बजाय आदि:



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. अगले अनुक्रम संख्या के साथ एक ग्रेल बनाई गई फ़ाइल में एक सम्मिलित कैसे सेट करें?

  2. postgresql में, विभाजन या एकाधिक डेटाबेस अधिक कुशल हैं?

  3. बड़ी मात्रा में डेटा के लिए PostgreSQL को स्केल करना

  4. PostgreSQL - सहसंबद्ध उप-क्वेरी विफल?

  5. स्प्रिंग बूट में डेटाबेस स्कीमा को ठीक से कैसे निर्दिष्ट करें?