PostgreSQL में पंक्ति मौजूद होने पर सबसे तेज़ जाँच करें


177

मेरे पास पंक्तियों का एक गुच्छा है जिसे मुझे तालिका में सम्मिलित करने की आवश्यकता है, लेकिन ये आवेषण हमेशा बैचों में किए जाते हैं। इसलिए मैं जांचना चाहता हूं कि क्या बैच से एक भी पंक्ति तालिका में मौजूद है क्योंकि तब मुझे पता है कि वे सभी सम्मिलित किए गए थे।

तो यह एक प्राथमिक कुंजी जांच नहीं है, लेकिन बहुत ज्यादा मायने नहीं रखना चाहिए। मैं केवल एकल पंक्ति की जाँच करना चाहूंगा ताकि count(*)शायद यह अच्छा न हो, इसलिए इसका कुछ ऐसा है जैसा existsमुझे लगता है।

लेकिन चूंकि मैं PostgreSQL के लिए काफी नया हूं, इसलिए मैं उन लोगों से पूछना चाहता हूं जो जानते हैं।

मेरे बैच में निम्नलिखित संरचना वाली पंक्तियाँ हैं:

userid | rightid | remaining_count

इसलिए यदि तालिका में कोई पंक्तियाँ हैं, useridतो इसका अर्थ है कि वे सभी वहाँ मौजूद हैं।


आप यह देखना चाहते हैं कि क्या तालिका में आपके बैच से कोई पंक्तियाँ, या कोई पंक्तियाँ हैं?
जेएनके

मेरे बैच से कोई पंक्तियाँ हाँ। वे सभी एक ही क्षेत्र बीमार साझा एक छोटे से संपादित करें।
वैलेंटाइन कुजूब

कृपया अपना प्रश्न स्पष्ट करें। आप रिकॉर्ड्स का एक बैच जोड़ना चाहते हैं, सभी या कुछ भी नहीं? क्या गिनती के बारे में कुछ खास है? (बीटीडब्ल्यू एक आरक्षित शब्द, एक स्तंभ नाम के रूप में अव्यावहारिक)
वाइल्डप्लासेर

ठीक है, मैं वास्तविक स्थिति को थोड़ा सरल बनाने की कोशिश कर रहा था लेकिन हम वास्तविक कार्यान्वयन के करीब और करीब आ रहे हैं। एक बार जब वे पंक्तियाँ सम्मिलित हो जाती हैं (किसी अन्य फ़ील्ड for_date के लिए) तो मैं निर्दिष्ट उपयोगकर्ता के लिए अधिकारों को कम करना शुरू कर देता हूं क्योंकि वे विशिष्ट अधिकारों का उपयोग करते हैं, एक बार अधिकार बन जाने के बाद वे उस तिथि तक उन कार्यों को नहीं कर सकते हैं। असली कहानी
वैलेंटाइन कुजब

1
बस तालिका की परिभाषा (प्रासंगिक हिस्सा) दिखाएं, और बताएं कि आप क्या करने का इरादा रखते हैं।
Wildplasser

जवाबों:


345

TRUE / FALSE वापसी के लिए EXISTS कुंजी शब्द का उपयोग करें:

select exists(select 1 from contact where id=12)

21
इस पर विस्तार, आप आसान संदर्भ के लिए दिए गए कॉलम का नाम दे सकते हैं। जैसेselect exists(select 1 from contact where id=12) AS "exists"
रोवन

3
यह बेहतर है, क्योंकि यह कभी-कभी (आपकी प्रोग्रामिंग भाषा के आधार पर) के बजाय एक मान (सच्चा या गलत) लौटाएगा, जो आपके द्वारा अपेक्षित तरीके का विस्तार नहीं कर सकता है।
isallw

1
इस विधि का उपयोग करने के साथ मेरे पास Seq स्कैन है। मैं कुछ गलत करता हूं?
फिफ्टी

2
@ Michael.MI की 30 लाख पंक्तियों के साथ DB तालिका है और जब मैं उपयोग करता हूं existsया limit 1मेरे पास मजबूत प्रदर्शन ड्रॉप होता है क्योंकि Postgres Index Scan के बजाय Seq Scan का उपयोग करता है। और analyzeमदद नहीं करता है।
फिफ्टी

2
@maciek कृपया यह समझें कि 'id' एक प्राथमिक कुंजी है, इसलिए "LIMIT 1" व्यर्थ होगा क्योंकि उस आईडी के साथ केवल एक रिकॉर्ड है
StartupGuy

34

कैसे के बारे में बस:

select 1 from tbl where userid = 123 limit 1;

जहाँ 123आप डालने वाले हैं उस बैच के उपयोगकर्ता का नाम

उपरोक्त क्वेरी या तो खाली सेट या एकल पंक्ति पर वापस आ जाएगी, जो इस बात पर निर्भर करता है कि दिए गए उपयोगकर्ता के साथ रिकॉर्ड हैं या नहीं।

यदि यह बहुत धीमा हो जाता है, तो आप एक इंडेक्स बना सकते हैं tbl.userid

यदि बैच से एकल पंक्ति भी तालिका में मौजूद है, तो उस स्थिति में मुझे अपनी पंक्तियाँ सम्मिलित करने की आवश्यकता नहीं है क्योंकि मुझे पता है कि वे सभी सम्मिलित हैं।

यदि आपका प्रोग्राम मिड-बैच से बाधित हो जाता है, तो भी यह सच है, मैं आपको यह सुनिश्चित करने की सलाह दूंगा कि आप डेटाबेस लेनदेन को उचित तरीके से प्रबंधित करें (यानी कि पूरा बैच एक ही लेनदेन में सम्मिलित हो जाए)।


11
यह कभी-कभी प्रोग्रामेटिक रूप से "सेलेक्ट (* 1) (सेलेक्ट 1 ... लिमिट 1)" से आसान हो सकता है क्योंकि यह हमेशा 0 या 1. के काउंट (*) के मान के साथ एक पंक्ति को वापस करने की गारंटी है
डेविड एल्ड्रिज

@DavidAldridge काउंट (*) का अभी भी मतलब है कि सभी पंक्तियों को पढ़ना होगा, जबकि सीमा 1 पहले रिकॉर्ड पर रुकती है और लौटती है
इमरान

3
@ इमरान मुझे लगता है कि आपने प्रश्न का गलत अर्थ निकाला है। COUNTएक नेस्टेड पर काम करता है SELECT(क्योंकि अधिक से अधिक 1 पंक्ति है LIMITसबक्वेरी में है)।
jpmc26

9
INSERT INTO target( userid, rightid, count )
  SELECT userid, rightid, count 
  FROM batch
  WHERE NOT EXISTS (
    SELECT * FROM target t2, batch b2
    WHERE t2.userid = b2.userid
    -- ... other keyfields ...
    )       
    ;

BTW: यदि आप चाहते हैं कि पूरा बैच नकल के मामले में फेल हो जाए , तो (एक प्राथमिक कुंजी बाधा दी गई)

INSERT INTO target( userid, rightid, count )
SELECT userid, rightid, count 
FROM batch
    ;

ठीक वही करेंगे जो आप चाहते हैं: या तो यह सफल होता है, या यह विफल होता है।


यह प्रत्येक पंक्ति की जाँच करेगा। वह सिंगल चेक करना चाहते हैं।
जेएनके

1
नहीं, यह एकल जाँच करता है। उपशम असंबद्ध है। एक बार मैचिंग जोड़ी मिल जाने पर यह जमानत कर देगा।
वाइल्डप्लासेर

आप सही हैं, मैंने सोचा कि यह बाहरी क्वेरी को संदर्भित करता है। +1 टू
यू

BTW: चूंकि क्वेरी एक लेन-देन के अंदर होती है, डुप्लिकेट आईडी डालने के लिए कुछ भी नहीं होगा, इसलिए उपश्रेणी को छोड़ा जा सकता है।
वाइल्डपलासर

हम्म मुझे यकीन नहीं है मैं समझ रहा हूँ। अधिकार सम्मिलित होने के बाद, मैं गिनती कॉलम को घटाना शुरू करता हूं। (चित्र के लिए बस कुछ विवरण) यदि पंक्तियाँ पहले से मौजूद हैं और सबक्विरी छोड़ दी गई हैं, तो मुझे लगता है कि बीमार को डुप्लिकेट अद्वितीय कुंजी के साथ त्रुटियां मिलती हैं या? (userid & right form कि यूनिक की।)
Valentin Kuzub

1
select true from tablename where condition limit 1;

मेरा मानना ​​है कि यह वह क्वेरी है जो विदेशी कुंजियों की जांच के लिए पोस्टग्रेज का उपयोग करती है।

आपके मामले में, आप इसे एक बार में भी कर सकते हैं:

insert into yourtable select $userid, $rightid, $count where not (select true from yourtable where userid = $userid limit 1);

1

@MikeM ने बताया।

select exists(select 1 from contact where id=12)

संपर्क पर सूचकांक के साथ , यह आमतौर पर समय की लागत को 1 एमएस तक घटा सकता है।

CREATE INDEX index_contact on contact(id);

0
SELECT 1 FROM user_right where userid = ? LIMIT 1

यदि आपके परिणाम में एक पंक्ति है तो आपको सम्मिलित करने की आवश्यकता नहीं है। अन्यथा अपने रिकॉर्ड डालें।


अगर गुच्छा में 100 पंक्तियाँ हैं तो यह मुझे 100 पंक्तियाँ लौटाएगा, आपको लगता है कि यह अच्छा है?
वैलेंटाइन कुजब

आप इसे 1 पंक्ति में सीमित कर सकते हैं। बेहतर प्रदर्शन करना चाहिए। इसके लिए @aix से संपादित उत्तर पर एक नज़र डालें।
फेबियन बार्नी

0

यदि आप प्रदर्शन के बारे में सोचते हैं, तो हो सकता है कि आप "पेरफ़ॉर्म" का उपयोग इस तरह से कर सकते हैं:

 PERFORM 1 FROM skytf.test_2 WHERE id=i LIMIT 1;
  IF FOUND THEN
      RAISE NOTICE ' found record id=%', i;  
  ELSE
      RAISE NOTICE ' not found record id=%', i;  
 END IF;

मेरे साथ काम नहीं करता है: मुझे प्रदर्शन के पास एक सिंटैक्स त्रुटि मिलती है
साइमन

1
कि pl / pgsql, SQL नहीं है, इसलिए "PERFORM" के लिए वाक्यविन्यास त्रुटि अगर इसे SQL के रूप में चलाने की कोशिश कर रहा है
मार्क K Cowan

-1

मैं विशेष रूप से आपकी सजा को संबोधित करने के लिए एक और विचार का प्रस्ताव करना चाहता हूं: "इसलिए मैं यह जांचना चाहता हूं कि क्या बैच से एक भी पंक्ति तालिका में मौजूद है क्योंकि तब मुझे पता है कि वे सभी सम्मिलित किए गए थे ।"

आप "बैचों" में सम्मिलित करके चीजों को कुशल बना रहे हैं, लेकिन फिर एक समय में एक रिकॉर्ड की जाँच कर रहे हैं? यह मुझे सहज लगता है। इसलिए जब आप कहते हैं " आवेषण हमेशा बैचों में किया जाता है " तो मैं इसे लेता हूं मतलब आप एक सम्मिलित विवरण के साथ कई रिकॉर्ड डाल रहे हैं । आपको यह महसूस करने की आवश्यकता है कि Postgres ACID अनुरूप है। यदि आप एक सम्मिलित विवरण के साथ कई रिकॉर्ड (डेटा का एक बैच) डाल रहे हैं , तो यह जांचने की आवश्यकता नहीं है कि कुछ डाला गया था या नहीं। बयान या तो पास हो जाएगा या यह विफल हो जाएगा। सभी रिकॉर्ड डाले जाएंगे या कोई नहीं।

दूसरी ओर, यदि आपका C # कोड केवल एक "सेट" अलग-अलग इन्सर्ट स्टेटमेंट्स कर रहा है, उदाहरण के लिए, एक लूप में, और आपके दिमाग में, यह "बैच" है .. तो आपको वास्तव में इसे "के रूप में वर्णित नहीं" करना चाहिए। आवेषण हमेशा बैचों में किया जाता है ”। यह तथ्य कि आप उम्मीद करते हैं कि आप जिसे "बैच" कहते हैं, वह हिस्सा वास्तव में डाला नहीं जा सकता है, और इसलिए चेक की आवश्यकता महसूस होती है, दृढ़ता से यह सुझाव देता है कि यह मामला है, जिस स्थिति में आपको अधिक मौलिक समस्या है। वास्तव में एक रिकॉर्ड के साथ कई रिकॉर्ड डालने के लिए आपको अपने प्रतिमान को बदलने की जरूरत है, और यदि व्यक्तिगत रिकॉर्ड ने इसे बनाया है, तो यह जाँच करेगा।

इस उदाहरण पर विचार करें:

CREATE TABLE temp_test (
    id SERIAL PRIMARY KEY,
    sometext TEXT,
    userid INT,
    somethingtomakeitfail INT unique
)
-- insert a batch of 3 rows
;;
INSERT INTO temp_test (sometext, userid, somethingtomakeitfail) VALUES
('foo', 1, 1),
('bar', 2, 2),
('baz', 3, 3)
;;
-- inspect the data of what we inserted
SELECT * FROM temp_test
;;
-- this entire statement will fail .. no need to check which one made it
INSERT INTO temp_test (sometext, userid, somethingtomakeitfail) VALUES
('foo', 2, 4),
('bar', 2, 5),
('baz', 3, 3)  -- <<--(deliberately simulate a failure)
;;
-- check it ... everything is the same from the last successful insert ..
-- no need to check which records from the 2nd insert may have made it in
SELECT * FROM temp_test

यह वास्तव में किसी भी ACID आज्ञाकारी DB के लिए प्रतिमान है .. न कि केवल Postgresql के लिए। दूसरे शब्दों में, यदि आप अपनी "बैच" अवधारणा को ठीक कर लेते हैं और पहले स्थान पर पंक्ति जाँच द्वारा कोई भी पंक्ति करने से बचते हैं तो आप बेहतर हैं।

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