आप FROM क्लॉज़ में अपडेट के लिए लक्ष्य तालिका निर्दिष्ट नहीं कर सकते


379

मेरे पास एक सरल mysql तालिका है:

CREATE TABLE IF NOT EXISTS `pers` (
  `persID` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(35) NOT NULL,
  `gehalt` int(11) NOT NULL,
  `chefID` int(11) DEFAULT NULL,
  PRIMARY KEY (`persID`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;

INSERT INTO `pers` (`persID`, `name`, `gehalt`, `chefID`) VALUES
(1, 'blb', 1000, 3),
(2, 'as', 1000, 3),
(3, 'chef', 1040, NULL);

मैंने निम्नलिखित अपडेट चलाने की कोशिश की, लेकिन मुझे केवल 1093 त्रुटि मिली:

UPDATE pers P 
SET P.gehalt = P.gehalt * 1.05 
WHERE (P.chefID IS NOT NULL 
OR gehalt < 
(SELECT (
    SELECT MAX(gehalt * 1.05) 
    FROM pers MA 
    WHERE MA.chefID = MA.chefID) 
    AS _pers
))

मैंने त्रुटि के लिए खोज की और mysql से अगले पृष्ठ http://dev.mysql.com/doc/refman/5.1/en/subquery-restrictions.html पर पाया , लेकिन यह मेरी मदद नहीं करता है।

मैं sql क्वेरी को सही करने के लिए क्या करूंगा?


जवाबों:


768

समस्या यह है कि MySQL, जो कुछ भी अकारण है, के लिए आपको इस तरह से प्रश्न लिखने की अनुमति नहीं है:

UPDATE myTable
SET myTable.A =
(
    SELECT B
    FROM myTable
    INNER JOIN ...
)

यदि आप किसी टेबल पर UPDATE/ INSERT/ कर रहे हैं, तो आप DELETEउस तालिका को आंतरिक क्वेरी में संदर्भित नहीं कर सकते (आप फिर भी उस बाहरी तालिका से फ़ील्ड का संदर्भ ले सकते हैं ...)


इसका समाधान myTableउप-क्वेरी के उदाहरण को (SELECT * FROM myTable)इस तरह बदलना है

UPDATE myTable
SET myTable.A =
(
    SELECT B
    FROM (SELECT * FROM myTable) AS something
    INNER JOIN ...
)

यह स्पष्ट रूप से आवश्यक क्षेत्रों को अस्थायी रूप से एक अस्थायी तालिका में कॉपी करने का कारण बनता है, इसलिए इसकी अनुमति है।

मैं इस समाधान पाया यहाँ । उस लेख से एक नोट:

आप SELECT * FROM tableवास्तविक जीवन में सिर्फ उपवर्ग में नहीं जाना चाहते हैं; मैं सिर्फ उदाहरणों को सरल रखना चाहता था। वास्तव में, आपको केवल उस अंतरतम क्वेरी में आवश्यक कॉलम का चयन करना चाहिए, और WHEREपरिणामों को सीमित करने के लिए एक अच्छा क्लॉज भी जोड़ना चाहिए।


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

35
@ साइराइड: अन्य डेटाबेस, जैसे कि MSSQL या Oracle, में यह मनमाना प्रतिबंध नहीं है
BlueRaja - Danny Pflughoeft

3
@ BlueRaja-DannyPflughoeft: यह मनमाना नहीं है। यह विकल्पों की लागत के आधार पर उचित डिजाइन निर्णय है। अन्य DB प्रणालियों ने वैसे भी उन लागतों से निपटने के लिए चुना। लेकिन वे सिस्टम नहीं हैं, उदाहरण के लिए, जब आप ग्रुप BY का उपयोग करते हैं, तो आप चयनित सूचियों में गैर-एकत्रित कॉलम शामिल करते हैं, और MySQL करता है। मैं तर्क दूंगा कि MySQL यहां गलत है, और मैं दूसरे स्टेटमेंट्स के लिए अन्य DBMS के समान कह सकता हूं।
१३'१३

33
@ साइराइड एक संबंधपरक बीजगणित के दृष्टिकोण से, Tऔर (SELECT * FROM T)पूरी तरह से समकक्ष हैं। वही संबंध हैं। इसलिए यह एक मनमाना, अमानवीय प्रतिबंध है। अधिक विशेष रूप से, यह MySQL को कुछ ऐसा करने के लिए मजबूर करना है जो यह स्पष्ट रूप से कर सकता है, लेकिन किसी कारण से यह अपने सरल रूप में पार्स नहीं कर सकता है।
टोबिया

4
मेरे मामले में स्वीकृत समाधान काम नहीं आया क्योंकि मेरी मेज बस बहुत बड़ी थी। क्वेरी कभी पूरी नहीं हुई। जाहिरा तौर पर यह कई आंतरिक संसाधनों को ले रहा है। इसके बजाय मैंने आंतरिक क्वेरी के साथ एक दृश्य बनाया और डेटा चयन के लिए इसका उपयोग किया, जिसने बिल्कुल ठीक काम किया। DELETE FROM t WHERE tableID NOT IN (SELECT viewID FROM t_view);इसके अलावा, मैं OPTIMIZE TABLE t;तालिका के आकार को कम करने के लिए बाद में चलने की सलाह देता हूं ।
कोडएक्स

53

आप इसे तीन चरणों में बना सकते हैं:

CREATE TABLE test2 AS
SELECT PersId 
FROM pers p
WHERE (
  chefID IS NOT NULL 
  OR gehalt < (
    SELECT MAX (
      gehalt * 1.05
    )
    FROM pers MA
    WHERE MA.chefID = p.chefID
  )
)

...

UPDATE pers P
SET P.gehalt = P.gehalt * 1.05
WHERE PersId
IN (
  SELECT PersId
  FROM test2
)
DROP TABLE test2;

या

UPDATE Pers P, (
  SELECT PersId
  FROM pers p
  WHERE (
   chefID IS NOT NULL 
   OR gehalt < (
     SELECT MAX (
       gehalt * 1.05
     )
     FROM pers MA
     WHERE MA.chefID = p.chefID
   )
 )
) t
SET P.gehalt = P.gehalt * 1.05
WHERE p.PersId = t.PersId

16
खैर, अधिकांश उप-वृतियों को CREATE TABLEबयानों के साथ कई चरणों के रूप में फिर से लिखा जा सकता है - मुझे उम्मीद है कि लेखक को इस बारे में पता था। हालाँकि, क्या यह एकमात्र समाधान है? या क्वेरी को सबक्वेरी या जॉइन के साथ फिर से लिखा जा सकता है? और क्यों (नहीं) ऐसा करते हो?
कोनरक

मुझे लगता है कि आपके दूसरे समाधान में आपके पास एक पूंजीकरण त्रुटि है। UPDATE Pers Pपढ़ना नहीं चाहिए UPDATE pers P?
यूबिकिबाकॉन

2
इस समाधान की कोशिश की और अस्थायी / दूसरी तालिका में बड़ी संख्या में प्रविष्टियों के लिए क्वेरी बहुत धीमी हो सकती है; अनुक्रमणिका / प्राथमिक कुंजी के साथ अस्थायी / दूसरी तालिका बनाने का प्रयास करें [ dev.mysql.com/doc/refman/5.1/en/create-table-select.html ] देखें
एलेक्स

जैसा कि @Konerak बताता है, यह वास्तव में सबसे अच्छा उत्तर नहीं है। नीचे ब्लूराज का जवाब मुझे सबसे अच्छा लगता है। Upvotes सहमत प्रतीत होते हैं।
शतयूट

@Konerak, CREATE TABLE AS SELECTभयानक प्रदर्शन नहीं देता है?
पैशियर

27

माईसकल में, आप एक ही तालिका को उप-तालिका द्वारा एक तालिका अपडेट नहीं कर सकते।

आप क्वेरी को दो भागों में अलग कर सकते हैं, या कर सकते हैं

 अद्यतन TABLE_A AS के रूप में
 IN JOIN TABLE_A AS B पर A.field1 = B.field1
 SET फ़ील्ड 2 =? 

5
SELECT ... SET? मैंने इस बारे में कभी नहीं सुना।
सर्ज एस

@grisson स्पष्टीकरण के लिए धन्यवाद। अब मुझे पता है कि मेरा IN क्लॉज काम क्यों नहीं करता है - मैं उसी तालिका को लक्षित कर रहा था।
एंथोनी

2
... यह वास्तव में काम नहीं लगता है। यह अभी भी मुझे वही त्रुटि दे रहा है।
ब्लूराजा - डैनी पफ्लुघोफ्ट

2
यह उत्तर वास्तव में अधिक सही और कुशल बात करता है, जिसका उपयोग AS Bदूसरे संदर्भ में किया जाता है TABLE_A। सबसे अपवित्र उदाहरण में जवाब AS Tको संभावित अक्षम के बजाय का उपयोग करके सरल बनाया जा सकता है FROM (SELECT * FROM myTable) AS something, जो सौभाग्य से क्वेरी ऑप्टिमाइज़र आमतौर पर समाप्त कर देता है, लेकिन हमेशा ऐसा नहीं कर सकता है।
नटब्रॉन

23

एक सबक्वेरी से एक अस्थायी टेबल (अस्थायी) बनाएं

UPDATE pers P 
SET P.gehalt = P.gehalt * 1.05 
WHERE P.persID IN (
    SELECT tempP.tempId
    FROM (
        SELECT persID as tempId
        FROM pers P
        WHERE
            P.chefID IS NOT NULL OR gehalt < 
                (SELECT (
                    SELECT MAX(gehalt * 1.05) 
                    FROM pers MA 
                    WHERE MA.chefID = MA.chefID) 
                    AS _pers
                )
    ) AS tempP
)

मैंने एक अलग नाम (उपनाम) पेश किया है और अस्थायी तालिका के लिए 'persID' कॉलम को एक नया नाम दिया है


इनर इंटरनल सेलेक्ट करने के बजाय वैरिएबल में वैल्यूज़ का चयन क्यों नहीं करते?
पैशियर

SELECT ( SELECT MAX(gehalt * 1.05)..- पहला SELECTकिसी भी कॉलम का चयन नहीं करता है।
इस्तियाक अहमद

18

यह काफी सरल है। उदाहरण के लिए, लिखने के बजाय:

INSERT INTO x (id, parent_id, code) VALUES (
    NULL,
    (SELECT id FROM x WHERE code='AAA'),
    'BBB'
);

आपको लिखना चाहिए

INSERT INTO x (id, parent_id, code)
VALUES (
    NULL,
    (SELECT t.id FROM (SELECT id, code FROM x) t WHERE t.code='AAA'),
    'BBB'
);

या इसी के समान।


13

BlueRaja द्वारा पोस्ट किया गया दृष्टिकोण धीमा है मैंने इसे संशोधित कर दिया है क्योंकि मैं तालिका से डुप्लिकेट को हटाने के लिए उपयोग कर रहा था। मामले में यह किसी के साथ मदद करता है बड़ी तालिका मूल क्वेरी

delete from table where id not in (select min(id) from table group by field 2)

इसमें अधिक समय लग रहा है:

DELETE FROM table where ID NOT IN(
  SELECT MIN(t.Id) from (select Id,field2 from table) AS t GROUP BY field2)

तेजी से समाधान

DELETE FROM table where ID NOT IN(
   SELECT x.Id from (SELECT MIN(Id) as Id from table GROUP BY field2) AS t)

एक टिप्पणी जोड़ें यदि आप नीच हैं।
अजाक 6


3

यदि आप तालिका से फ़ील्ड को पढ़ने और उसी तालिका पर फ़ील्डबी पर सहेजने का प्रयास कर रहे हैं, तो फ़ील्ड = फ़ील्ड को आप इस पर विचार कर सकते हैं।

UPDATE tableA,
    tableA AS tableA_1 
SET 
    tableA.fieldB= tableA_1.filedA
WHERE
    (((tableA.conditionFild) = 'condition')
        AND ((tableA.fieldc) = tableA_1.fieldd));

जब कोड-फ़ील्ड फ़ील्ड से मान की प्रतिलिपि करता है, तो स्थिति-फ़ील्ड आपकी स्थिति से मिलती है। यह भी ADO में काम करता है (जैसे पहुँच)

स्रोत: खुद की कोशिश की


3

MariaDB ने इसे 10.3.x (दोनों के लिए DELETEऔर UPDATE) से शुरू किया है :

अद्यतन - समान स्रोत और लक्ष्य के साथ विवरण

मारियाडीबी 10.3.2 से, अद्यतन स्टेटमेंट में एक ही स्रोत और लक्ष्य हो सकता है।

MariaDB 10.3.1 तक, निम्नलिखित अद्यतन कथन काम नहीं करेगा:

UPDATE t1 SET c1=c1+1 WHERE c2=(SELECT MAX(c2) FROM t1);
  ERROR 1093 (HY000): Table 't1' is specified twice, 
  both as a target for 'UPDATE' and as a separate source for data

MariaDB 10.3.2 से, कथन सफलतापूर्वक निष्पादित होता है:

UPDATE t1 SET c1=c1+1 WHERE c2=(SELECT MAX(c2) FROM t1);

DELETE - समान स्रोत और लक्ष्य तालिका

MariaDB 10.3.1 तक, समान स्रोत और लक्ष्य वाली तालिका से हटाना संभव नहीं था। मारियाबीडी 10.3.1 से, अब यह संभव है। उदाहरण के लिए:

DELETE FROM t1 WHERE c1 IN (SELECT b.c1 FROM t1 b WHERE b.c2=0);

DBFiddle MariaDB 10.2 - त्रुटि

DBFiddle MariaDB 10.3 - सफलता


0

अन्य वर्कअराउंड में उपकुंजी में SELECT DISTINCT या LIMIT का उपयोग करना शामिल है, हालांकि ये भौतिकीकरण के रूप में उनके प्रभाव के रूप में स्पष्ट नहीं हैं। यह मेरे लिए काम किया

जैसा कि MySql Doc में बताया गया है

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