2017-04-06 4 views
7

मैं से संबंधित सभी events को पुनर्प्राप्त करने के लिए सबसे सरल, सबसे कुशल SQL क्वेरी लिखना चाहता हूं।एसक्यूएल में नेस्टेड रिश्तों के लिए आसानी से और कुशलतापूर्वक पूछताछ कैसे करें?


सेटअप

यहाँ मेरी स्कीमा कैसा दिखता है की एक साधारण प्रतिनिधित्व है:

enter image description here

कुछ बातें गौर करने योग्य

  • usersmemberships के माध्यम से teams से संबंध रखते हैं ।
  • teams कई collections, apps, और webhooks हो सकते हैं।
  • collections में कई webhooks भी हो सकते हैं।
  • webhooks या तो team या collection से संबंधित हो सकता है, लेकिन केवल एक ही।
  • events किसी भी वस्तु से संबंधित हो सकता है, लेकिन केवल एक ही।

यह एक काफी बुनियादी सेटअप की तरह लगता है कि अधिकांश सास-प्रकार की कंपनियों के पास (जैसे स्लैक या स्ट्रिप) होगा। टीमों द्वारा सब कुछ "स्वामित्व" है, लेकिन उपयोगकर्ता टीमों से संबंधित हैं और इंटरफेस के साथ बातचीत करते हैं।


समस्या

यह देखते हुए कि सेटअप, मैं एक SQL क्वेरी कि हल करती है बनाना चाहते हैं ...

कि घटनाओं से संबंधित हैं (प्रत्यक्ष या परोक्ष) के सभी का पता लगाएं करने के लिए id द्वारा दिया गया उपयोगकर्ता।

मैं आसानी से उन प्रश्नों को लिख सकता हूं जो किसी विशिष्ट माध्यम से प्रत्यक्ष या परोक्ष रूप से मिलते हैं। उदाहरण के लिए ...

घटनाओं सीधे एक उपयोगकर्ता से संबंधित id से कर रहे हैं के सभी का पता लगाएं।

SELECT * 
FROM events 
WHERE user_id = ${id} 

या ...

घटनाओं परोक्ष रूप से अपनी टीमों के माध्यम से एक उपयोगकर्ता से संबंधित हैं के सभी का पता लगाएं।

SELECT events.* 
FROM events 
JOIN memberships ON memberships.team_id = events.team_id 
WHERE memberships.user_id = ${id} 

या यहां तक ​​कि ...

घटनाओं परोक्ष रूप से एक उपयोगकर्ता से संबंधित अपनी टीमों में से किसी संग्रह के माध्यम से कर रहे हैं के सभी का पता लगाएं।

SELECT events.* 
FROM events 
JOIN collections ON collections.id = events.collection_id 
JOIN memberships ON memberships.team_id = collections.team_id 
WHERE memberships.user_id = ${id} 

Webhooks, एक अधिक जटिल हैं क्योंकि वे दो अलग अलग तरीकों से संबंधित हो सकता है ...

घटनाओं परोक्ष रूप से एक उपयोगकर्ता से संबंधित किसी भी webhooks के माध्यम से कर रहे हैं के सभी का पता लगाएं उनकी टीमों या संग्रहों का।

SELECT * 
FROM events 
WHERE webhook_id IN (
    SELECT webhooks.id 
    FROM webhooks 
    JOIN memberships ON memberships.team_id = webhooks.team_id 
    WHERE memberships.user_id = ${id} 
) 
OR webhook_id IN (
    SELECT webhooks.id 
    FROM webhooks 
    JOIN collections ON collections.id = webhooks.collection_id 
    JOIN memberships ON memberships.team_id = collections.team_id 
    WHERE memberships.user_id = ${id} 
) 

लेकिन जैसा कि आप देख सकते हैं, वहाँ के लिए एक उपयोगकर्ता एक घटना जो हुआ उन सभी रास्तों के माध्यम से, से संबंधित होना अलग अलग तरीकों का एक बहुत हैं! तो जब मैं एक प्रश्न है कि सफलतापूर्वक उन से संबंधित घटनाओं के सभी हो जाता है करने के लिए प्रयास करते हैं, यह की तरह लग रही समाप्त होता है ...

SELECT * 
FROM events 
WHERE user_id = ${id} 
OR app_id IN (
    SELECT apps.id 
    FROM apps 
    JOIN memberships ON memberships.team_id = apps.team_id 
    WHERE memberships.user_id = ${id} 
) 
OR collection_id IN (
    SELECT collections.id 
    FROM collections 
    JOIN memberships ON memberships.team_id = collections.team_id 
    WHERE memberships.user_id = ${id} 
) 
OR memberships_id IN (
    SELECT id 
    FROM memberships 
    WHERE user_id = ${id} 
) 
OR team_id IN (
    SELECT team_id 
    FROM memberships 
    WHERE user_id = ${id} 
) 
OR webhook_id IN (
    SELECT webhooks.id 
    FROM webhooks 
    JOIN memberships ON memberships.team_id = webhooks.team_id 
    WHERE memberships.user_id = ${id} 
) 
OR webhook_id IN (
    SELECT webhooks.id 
    FROM webhooks 
    JOIN collections ON collections.id = webhooks.collection_id 
    JOIN memberships ON memberships.team_id = collections.team_id 
    WHERE memberships.user_id = ${id} 
) 

प्रश्न

  • है कि अंतिम "सभी शामिल" क्वेरी बहुत अक्षम?
  • क्या इसे लिखने का एक और अधिक प्रभावी तरीका है?
  • क्या इसे लिखने के लिए एक आसान, आसान-पढ़ने-बाद का तरीका है?
+1

आपने यहां 3 अलग-अलग डेटाबेस सिस्टम टैग किए हैं, कृपया केवल एक के साथ चिपके रहें। – DavidG

+2

यह मेरे मित्र का एक अच्छी तरह से लिखित प्रश्न है, और मैं देख सकता हूं कि आपने इसे स्वयं हल करने का प्रयास किया है, यह दर्शाता है कि आपने इसमें काम किया है। –

उत्तर

4

एकमात्र चीज जिसे मैं सोच सकता हूं वह यूनियनों का उपयोग करना तेज़ी से कर देगा।

SELECT e.* 
FROM events e 
WHERE user_id = ${id} 
UNION 
select e.* 
    FROM apps a 
    join events e on a.apps_id = e.apps_id 
    JOIN memberships ON memberships.team_id = apps.team_id 
    WHERE memberships.user_id = ${id} 
UNION 
select e.* 
from 
    FROM collections c 
    join events e on e.collections_id = c.collections_id 
    JOIN memberships ON memberships.team_id = collections.team_id 
    WHERE memberships.user_id = ${id} 
UNION 
select e.* 
    FROM memberships m 
    join events e on e.memberships_id = e.memberships_id 
    WHERE user_id = ${id} 
UNION 
...; 
+1

क्या आप यहां यूनियन या यूनियन चाहते हैं? यूनियन का तात्पर्य है कि प्रश्न पारस्परिक रूप से अनन्य नहीं हैं लेकिन परिणाम अद्वितीय होना चाहिए। आम तौर पर क्वेरी में एक तरह का कदम होता है। यूनियन सभी का तात्पर्य है कि या तो आपको डुप्लिकेट नतीजे होने पर कोई फर्क नहीं पड़ता है या कि सबक्व्यूरीज़ परस्पर अनन्य होने की गारंटी है (इस मामले में) और इसलिए आमतौर पर अतिरिक्त प्रकार शामिल नहीं होता है, और तेज़ होता है। – joshp

+0

चाहे यह मूल "OR .... IN" क्वेरी से तेज़ है, शायद विशिष्ट डेटाबेस पर बहुत निर्भर करता है। – joshp

5

किसी भी प्रश्न के साथ, सबसे प्रभावी विधि "यह निर्भर करती है"। खेल में कई चर हैं - पंक्तियों की पंक्तियों, पंक्ति लंबाई, सूचकांक मौजूद हैं, सर्वर पर रैम इत्यादि।

इस तरह की समस्या को संभालने का सबसे अच्छा तरीका (सोच रखने की क्षमता और दक्षता के लिए एक बहादुर दृष्टिकोण) सीटीई का उपयोग करके है, जो आपको एक अस्थायी परिणाम बनाने की अनुमति देता है और परिणामस्वरूप आपकी क्वेरी में पुन: उपयोग करता है। सीटीई कीवर्ड के साथ उपयोग करते हैं, और अनिवार्य रूप से, एक मेज के रूप में एक परिणाम उर्फ ​​ताकि आप इसके खिलाफ शामिल हों सकते हैं कई बार:

WITH user_memberships AS (
    SELECT * 
    FROM memberships 
    WHERE user_id = ${id} 
), user_apps AS (
    SELECT * 
    FROM apps 
    INNER JOIN user_memberships 
     ON user_memberships.team_id = apps.team_id 
), user_collections AS (
    SELECT * 
    FROM collections 
    INNER JOIN user_memberships 
     ON user_memberships.team_id = collections.team_id 
), user_webhooks AS (
    SELECT * 
    FROM webhooks 
    LEFT OUTER JOIN user_collections ON user_collections.id = webhooks.collection_id 
    INNER JOIN user_memberships 
     ON user_memberships.team_id = webhooks.team_id 
     OR user_memberships.team_id = user_collections.team_id 
) 

SELECT events.* 
FROM events 
WHERE app_id IN (SELECT id FROM user_apps) 
OR collection_id IN (SELECT id FROM user_collections) 
OR membership_id IN (SELECT id FROM user_memberships) 
OR team_id IN (SELECT team_id FROM user_memberships) 
OR user_id = ${id} 
OR webhook_id IN (SELECT id FROM user_webhooks) 
; 

यह इस तरह से करने के लाभ हैं:

  1. प्रत्येक CTE कर सकते हैं उपयुक्त जॉइन पर एक इंडेक्स का लाभ उठाएं और निष्पादन योजनाकार जटिल भविष्यवाणियों की श्रृंखला को हल करने के प्रयास के बजाय केवल उपसभापति के लिए परिणाम लौटाएं
  2. सीटीई को व्यक्तिगत रूप से बनाए रखा जा सकता है, जिससे सबसेट आसान समस्या निवारण समस्याएं
  3. आप सूखी सिद्धांत
  4. CTE क्वेरी के बाहर मान है, तो उल्लंघन नहीं कर रहे हैं, आप एक संग्रहीत प्रक्रिया और संदर्भ है कि बजाय
3

मैं नहीं जानता कि आप कितना नियंत्रण में ले जा सकते हैं आपकी स्कीमा पर अगर उत्तर "कोई नहीं" है तो आगे पढ़ें। यदि आप अपनी स्थिति के लिए उचित नहीं हैं, तो मैं यहां बहुत अधिक जानकारी नहीं दे रहा हूं लेकिन यह मेरे लिए स्वामित्व मॉडल जैसा दिखता है।

यानी।

BaseTable

ईद

IdOwner (BaseTable पर ईद के लिए FK - बहुत महत्वपूर्ण)

प्रकार (उपयोगकर्ता = 0, अनुप्रयोग = 1, संग्रह = 2 आदि या एक गणन का उपयोग)

अनुप्रयोग

आईडी (आधार करने के लिए FK टेबल)

संग्रह

आईडी (BaseTable को FK)

सदस्यता

आईडी (BaseTable को FK)

Webhooks

आईडी (FK टी ओ BaseTable)

टीम

आईडी (FK BaseTable को)

घटनाक्रम

आईडी (BaseTable को FK)

सदस्यता

Team_Id (FK Basetable या टीम)

user_id (FK को Basetable करने के लिए या उपयोगकर्ता)

उपयोगकर्ता

आईडी (BaseTable को FK)

तो आपकी क्वेरी एक पुनरावर्ती CTE हो जाता है: "का पता लगाएं मुझे ऑब्जेक्ट एक्स के स्वामित्व वाले सभी ऑब्जेक्ट्स - या अंततः उपयोगकर्ता एक्स "

जो आपको आईडी की एक सूची देगा जो आपको अपनी घटनाओं तालिका में शामिल होना होगा और आप अपनी वस्तुएं हैं

इस प्रकार का मॉडल थोड़ा बालों वाला होता है क्योंकि बेस टेबल के साथ आपको इसमें शामिल होने के लिए कुछ भी लोड करना पड़ता है लेकिन इस तरह के नेस्टेड स्वामित्व के लिए यह काफी अच्छी तरह से काम करता है।

मैं इसे एक टिप्पणी के रूप में पोस्ट करना चाहता हूं लेकिन अगर मैं स्वरूपण करता हूं तो गायब हो जाएगा इसलिए मैंने इसे एक उत्तर के रूप में पोस्ट किया है। अगर यह मदद करता है और आप थोड़ा और विस्तार चाहते हैं तो मुझे वापस आने के लिए स्वतंत्र महसूस करें।

अगर मैं पूरी तरह से इस बिंदु को याद कर चुका हूं और इससे मदद नहीं मिलती है तो कृपया मुझे चिल्लाओ (एसओ पर पहले था) बस "धन्यवाद, एडम, लेकिन इससे मदद नहीं मिलती" और मैं इसे मिटाओ।

दयालु संबंध,

एडम।

संबंधित मुद्दे