MySQL में कॉलम मानों की अदला-बदली


127

मेरे पास निर्देशांक के साथ एक MySQL तालिका है, स्तंभ नाम X और Y हैं। अब मैं इस तालिका में स्तंभ मानों को स्वैप करना चाहता हूं, ताकि X Y हो जाए और Y X बन जाए। सबसे स्पष्ट समाधान कॉलम का नाम बदलना होगा, लेकिन मैं जरूरी नहीं है कि मैं संरचना परिवर्तन करना चाहता हूं क्योंकि मेरे पास ऐसा करने की अनुमति नहीं है।

क्या यह किसी तरह से अद्यतन करना संभव है ? अद्यतन तालिका SET X = Y, Y = X स्पष्ट रूप से वह नहीं करेगी जो मैं चाहता हूं।


संपादित करें: कृपया ध्यान दें कि ऊपर उल्लिखित अनुमतियों पर मेरा प्रतिबंध, तालिका / डेटाबेस संरचना को बदलने वाले अन्य तालिका या अन्य आदेशों के प्रभावी रूप से उपयोग को रोकता है। कॉलम का नाम बदलना या नए जोड़ना दुर्भाग्य से विकल्प नहीं हैं।


5
एक नोट के रूप में, UPDATE table SET X = Y, Y = Xयह SQL में करने का मानक तरीका है, केवल MySQL दुर्व्यवहार करता है।
एंटटी हापाला

जवाबों:


204

मुझे बस उसी से निपटना था और मैं अपने निष्कर्षों को संक्षेप में बताऊंगा।

  1. UPDATE table SET X=Y, Y=Xदृष्टिकोण स्पष्ट रूप से काम नहीं करता, के रूप में यह सिर्फ वाई करने के लिए दोनों मूल्यों को निर्धारित करेंगे

  2. यहाँ एक विधि है जो एक अस्थायी चर का उपयोग करती है। "IS NOT NULL" ट्वीक के लिए http://beerpla.net/2009/02/02/17/swapping-column-values-in-mysql/ की टिप्पणियों से एंटनी को धन्यवाद । इसके बिना, क्वेरी अप्रत्याशित रूप से काम करती है। पोस्ट के अंत में टेबल स्कीमा देखें। यदि उनमें से एक NULL है तो यह विधि मानों को स्वैप नहीं करती है। # 3 विधि का उपयोग करें जिसमें यह सीमा नहीं है।

    UPDATE swap_test SET x=y, y=@temp WHERE (@temp:=x) IS NOT NULL;

  3. इस विधि की पेशकश डिपिन ने की थी, फिर भी, http://beerpla.net/2009/02/02/17/swapping-column-values-in-mysql/ की टिप्पणियाँ । मुझे लगता है कि यह सबसे सुंदर और साफ समाधान है। यह NULL और Non-NULL दोनों मानों के साथ काम करता है।

    UPDATE swap_test SET x=(@temp:=x), x = y, y = @temp;

  4. मुझे लगता है कि काम करने के लिए आया था एक और दृष्टिकोण:

    UPDATE swap_test s1, swap_test s2 SET s1.x=s1.y, s1.y=s2.x WHERE s1.id=s2.id;

अनिवार्य रूप से, 1 तालिका अपडेट की जा रही है और 2 का उपयोग पुराने डेटा को खींचने के लिए किया जाता है।
ध्यान दें कि इस दृष्टिकोण के लिए एक प्राथमिक कुंजी मौजूद होना आवश्यक है।

यह मेरा परीक्षण स्कीमा है:

CREATE TABLE `swap_test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `x` varchar(255) DEFAULT NULL,
  `y` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `swap_test` VALUES ('1', 'a', '10');
INSERT INTO `swap_test` VALUES ('2', NULL, '20');
INSERT INTO `swap_test` VALUES ('3', 'c', NULL);

25
जैसा कि MySQL डॉक्स में उल्लेख किया गया है, किसी एक स्टेटमेंट में वेरिएबल्स को असाइन करना और पढ़ना सुरक्षित नहीं है। संचालन के आदेश की गारंटी नहीं है। तो एकमात्र सुरक्षित तरीका # 4
AMIB

विकल्प 4 मेरे लिए काम किया। यदि आप केवल कुछ पंक्तियों के लिए स्तंभों को स्वैप करने की आवश्यकता है, तो आप स्पष्ट रूप से उस खंड में अधिक शर्तें जोड़ सकते हैं।
ब्रैड कैंपबेल

7
तुम्हें पता है, मैंने कभी नहीं सोचा था कि उस बेवकूफ साक्षात्कार प्रश्न के लिए एक व्यावहारिक उपयोग होगा जो एक अस्थायी का उपयोग किए बिना दो चर स्वैप करने के लिए कह रहा है, लेकिन यहां यह है, और पूर्णांकों के लिए यह वास्तव में काम करेगा: अद्यतन swap_test सेट x = x + y, y = xy, एक्स = xy;
akजक

इस जवाब का सबसे सीधा कॉपी / पेस्ट से है beerpla.net/2009/02/17/swapping-column-values-in-mysql

17
@ झाविंस ऐसा इसलिए है क्योंकि Beerpla.net मेरा ब्लॉग है।
आर्टेम रसाकोवस्की

52

आप राशि ले सकते हैं और एक्स और वाई का उपयोग कर विरोधी मूल्य को घटा सकते हैं

UPDATE swaptest SET X=X+Y,Y=X-Y,X=X-Y;

यहां एक नमूना परीक्षण किया गया है (और यह नकारात्मक संख्याओं के साथ काम करता है)

mysql> use test
Database changed
mysql> drop table if exists swaptest;
Query OK, 0 rows affected (0.03 sec)

mysql> create table swaptest (X int,Y int);
Query OK, 0 rows affected (0.12 sec)

mysql> INSERT INTO swaptest VALUES (1,2),(3,4),(-5,-8),(-13,27);
Query OK, 4 rows affected (0.08 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM swaptest;
+------+------+
| X    | Y    |
+------+------+
|    1 |    2 |
|    3 |    4 |
|   -5 |   -8 |
|  -13 |   27 |
+------+------+
4 rows in set (0.00 sec)

mysql>

यहां पर अदला-बदली की जा रही है

mysql> UPDATE swaptest SET X=X+Y,Y=X-Y,X=X-Y;
Query OK, 4 rows affected (0.07 sec)
Rows matched: 4  Changed: 4  Warnings: 0

mysql> SELECT * FROM swaptest;
+------+------+
| X    | Y    |
+------+------+
|    2 |    1 |
|    4 |    3 |
|   -8 |   -5 |
|   27 |  -13 |
+------+------+
4 rows in set (0.00 sec)

mysql>

कोशिश करो !!!


5
संख्याओं के लिए यह वास्तव में सबसे साफ है।
आपका कॉमन सेंस

यदि कोई मान जोड़ते समय ओवरफ्लो हो जाए तो समस्या हो सकती है?
टूलमेकर

@ToolmakerSteve शायद TINYINTया सही के विशाल मानों के लिए INT, आप सही हैं !!!
RolandoMySQLDBA

29

निम्नलिखित कोड मेरे त्वरित परीक्षण में सभी परिदृश्यों के लिए काम करता है:

UPDATE swap_test
   SET x=(@temp:=x), x = y, y = @temp

UPDATE table swap_test? यह नहीं होना चाहिए UPDATE swap_test?
पैंग

12

अद्यतन तालिका SET X = Y, Y = X ठीक वही करेगी जो आप चाहते हैं (संपादित करें: PostgreSQL में, MySQL नहीं, नीचे देखें)। मानों को पुरानी पंक्ति से लिया जाता है और उसी पंक्ति की नई प्रति को सौंपा जाता है, फिर पुरानी पंक्ति को बदल दिया जाता है। आपको एक अस्थायी तालिका, एक अस्थायी कॉलम, या अन्य स्वैप ट्रिक का उपयोग करने का सहारा नहीं लेना है।

@ D4V360: मैं देख रहा हूं। यह चौंकाने वाला और अप्रत्याशित है। मैं PostgreSQL का उपयोग करता हूं और मेरा उत्तर सही तरीके से वहां काम करता है (मैंने इसे आजमाया)। देखें PostgreSQL अद्यतन डॉक्स , जहां यह उल्लेख सेट खंड के दाहिने हाथ की ओर है कि भाव स्पष्ट रूप से कॉलम के पुराने मूल्यों का उपयोग (पैरामीटर, अभिव्यक्ति के तहत)। मैं देखता हूं कि संबंधित MySQL UPDATE डॉक्स में "सिंगल-टेबल UPDATE असाइनमेंट्स का आमतौर पर बाएं से दाएं मूल्यांकन किया जाता है" कथन होता है, जो आपके द्वारा वर्णित व्यवहार को दर्शाता है।

जानकार अच्छा लगा।


धन्यवाद ग्रेग और D4V360, अद्यतन प्रश्नों के व्यवहार के बारे में PostgreSQL और MySQL में अंतर जानने के लिए अच्छा है।
विजय देव

"X = y, y = x" दृष्टिकोण भी ओरेकल में काम करता है, जो इसके लायक है।
बुरहान अली

2
मैंने PostgreSQL और SET X = Y का उपयोग किया, Y = X ने मुझे बचाया :)
अनाम

4
IMHO यह उत्तर एक गड़बड़ है - "उफ़ मन कभी नहीं" के साथ बुरी सलाह। इसका आधा हिस्सा एक टिप्पणी होना चाहिए और शेष भाग का एकमात्र हिस्सा जो प्रश्न के लिए प्रासंगिक है, वह MySQL डॉक्स का लिंक है ...
एयर

6

ठीक है, तो सिर्फ मनोरंजन के लिए, आप ऐसा कर सकते हैं! (मान लें कि आप स्ट्रिंग मानों की अदला-बदली कर रहे हैं)

mysql> select * from swapper;
+------+------+
| foo  | bar  |
+------+------+
| 6    | 1    | 
| 5    | 2    | 
| 4    | 3    | 
+------+------+
3 rows in set (0.00 sec)

mysql> update swapper set 
    -> foo = concat(foo, "###", bar),
    -> bar = replace(foo, concat("###", bar), ""),
    -> foo = replace(foo, concat(bar, "###"), "");

Query OK, 3 rows affected (0.00 sec)
Rows matched: 3  Changed: 3  Warnings: 0

mysql> select * from swapper;
+------+------+
| foo  | bar  |
+------+------+
| 1    | 6    | 
| 2    | 5    | 
| 3    | 4    | 
+------+------+
3 rows in set (0.00 sec)

MySQL में बाएं से दाएं मूल्यांकन प्रक्रिया का दुरुपयोग करने का एक अच्छा सा मज़ा है।

वैकल्पिक रूप से, यदि वे संख्याएँ हैं तो बस XOR का उपयोग करें। आपने निर्देशांक का उल्लेख किया है, तो क्या आपके पास प्यारे पूर्णांक मान, या जटिल तार हैं?

संपादित करें: XOR सामान इस तरह से काम करता है:

update swapper set foo = foo ^ bar, bar = foo ^ bar, foo = foo ^ bar;

5

मेरा मानना ​​है कि एक मध्यवर्ती विनिमय चर इस तरह से सबसे अच्छा अभ्यास है:

update z set c1 = @c := c1, c1 = c2, c2 = @c

सबसे पहले, यह हमेशा काम करता है; दूसरा, यह डेटा प्रकार की परवाह किए बिना काम करता है।

दोनों के बावजूद

update z set c1 = c1 ^ c2, c2 = c1 ^ c2, c1 = c1 ^ c2

तथा

update z set c1 = c1 + c2, c2 = c1 - c2, c1 = c1 - c2

आमतौर पर काम कर रहे हैं, केवल संख्या डेटा प्रकार के लिए, और यह अतिप्रवाह को रोकने के लिए आपकी जिम्मेदारी है, आप हस्ताक्षर किए और अहस्ताक्षरित के बीच XOR का उपयोग नहीं कर सकते, आप ओवरफ्लो होने की संभावना के लिए राशि का उपयोग भी नहीं कर सकते।

तथा

update z set c1 = c2, c2 = @c where @c := c1

काम नहीं कर रहा है अगर c1 0 या NULL या शून्य लंबाई स्ट्रिंग या सिर्फ रिक्त स्थान है।

हमें इसे बदलने की जरूरत है

update z set c1 = c2, c2 = @c where if((@c := c1), true, true)

यहाँ स्क्रिप्ट है:

mysql> create table z (c1 int, c2 int)
    -> ;
Query OK, 0 rows affected (0.02 sec)

mysql> insert into z values(0, 1), (-1, 1), (pow(2, 31) - 1, pow(2, 31) - 2)
    -> ;
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from z;
+------------+------------+
| c1         | c2         |
+------------+------------+
|          0 |          1 |
|         -1 |          1 |
| 2147483647 | 2147483646 |
+------------+------------+
3 rows in set (0.02 sec)

mysql> update z set c1 = c1 ^ c2, c2 = c1 ^ c2, c1 = c1 ^ c2;
ERROR 1264 (22003): Out of range value for column 'c1' at row 2
mysql> update z set c1 = c1 + c2, c2 = c1 - c2, c1 = c1 - c2;
ERROR 1264 (22003): Out of range value for column 'c1' at row 3

mysql> select * from z;
+------------+------------+
| c1         | c2         |
+------------+------------+
|          0 |          1 |
|          1 |         -1 |
| 2147483646 | 2147483647 |
+------------+------------+
3 rows in set (0.02 sec)

mysql> update z set c1 = c2, c2 = @c where @c := c1;
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2  Changed: 2  Warnings: 0

mysql> select * from z;
+------------+------------+
| c1         | c2         |
+------------+------------+
|          0 |          1 |
|         -1 |          1 |
| 2147483647 | 2147483646 |
+------------+------------+
3 rows in set (0.00 sec)

mysql> select * from z;
+------------+------------+
| c1         | c2         |
+------------+------------+
|          1 |          0 |
|          1 |         -1 |
| 2147483646 | 2147483647 |
+------------+------------+
3 rows in set (0.00 sec)

mysql> update z set c1 = @c := c1, c1 = c2, c2 = @c;
Query OK, 3 rows affected (0.02 sec)
Rows matched: 3  Changed: 3  Warnings: 0

mysql> select * from z;
+------------+------------+
| c1         | c2         |
+------------+------------+
|          0 |          1 |
|         -1 |          1 |
| 2147483647 | 2147483646 |
+------------+------------+
3 rows in set (0.00 sec)

mysql>update z set c1 = c2, c2 = @c where if((@c := c1), true, true);
Query OK, 3 rows affected (0.02 sec)
Rows matched: 3  Changed: 3  Warnings: 0

mysql> select * from z;
+------------+------------+
| c1         | c2         |
+------------+------------+
|          1 |          0 |
|          1 |         -1 |
| 2147483646 | 2147483647 |
+------------+------------+
3 rows in set (0.00 sec)

अंत में बेवकूफ साक्षात्कार प्रश्न के लिए एक अच्छा उपयोग खोजने के लिए +1, जहां आपको अस्थायी के बिना दो चर स्वैप करना है ;-)
izak


4

ALTER TABLE table ADD COLUMN tmp;
UPDATE table SET tmp = X;
UPDATE table SET X = Y;
UPDATE table SET Y = tmp;
ALTER TABLE table DROP COLUMN tmp;
कुछ इस तरह?

संपादित करें: ग्रेग की टिप्पणी के बारे में: नहीं, यह काम नहीं करता है:

mysql> select * from test;
+------+------+
| x    | y    |
+------+------+
|    1 |    2 |
|    3 |    4 |
+------+------+
2 rows in set (0.00 sec)

mysql> update test set x=y, y=x; Query OK, 2 rows affected (0.00 sec) Rows matched: 2 Changed: 2 Warnings: 0

mysql> select * from test; +------+------+ | x | y | +------+------+ | 2 | 2 | | 4 | 4 | +------+------+ 2 rows in set (0.00 sec)


बस रिकार्ड के लिए: यह करता है , जबकि यह है PostgreSQL में काम नहीं MySQL में काम करते हैं।
str

2

यह निश्चित रूप से काम करता है! मुझे बस यूरो और एसकेके मूल्य स्तंभों को स्वैप करना आवश्यक है। :)

UPDATE tbl SET X=Y, Y=@temp where @temp:=X;

ऊपर काम नहीं करेगा (ERROR 1064 (42000): आपके SQL सिंटैक्स में एक त्रुटि है)


1

मान लें कि आपने अपने कॉलम में पूर्णांकों पर हस्ताक्षर किए हैं, तो आपको CAST (^ b AS SIGNED) का उपयोग करने की आवश्यकता हो सकती है, क्योंकि ^ ऑपरेटर का परिणाम MySQL में एक अहस्ताक्षरित 64-बिट पूर्णांक है।

यदि यह किसी की मदद करता है, तो यहां मैं एक ही कॉलम को दो दी गई पंक्तियों के बीच स्वैप करता हूं:

SELECT BIT_XOR(foo) FROM table WHERE key = $1 OR key = $2

UPDATE table SET foo = CAST(foo ^ $3 AS SIGNED) WHERE key = $1 OR key = $2

जहाँ $ 1 और $ 2 दो पंक्तियों की कुंजियाँ हैं और $ 3 पहली क्वेरी का परिणाम है।


1

मैंने इसे आजमाया नहीं है

UPDATE tbl SET @temp=X, X=Y, Y=@temp

कर सकते हैं।

निशान


1

आप कॉलम के नाम बदल सकते हैं, लेकिन यह हैक का अधिक है। लेकिन इन स्तंभों पर हो सकने वाले किसी भी सूचकांक से सावधान रहें


1

टेबल का नाम ग्राहक है। फ़ील्ड a और b हैं, मान को b पर स्वैप करें;

अद्यतन ग्राहक सेट करें a = (@ temp: = a), a = b, b = @temp

मैंने जाँच की यह ठीक काम कर रहा है।


1

SQL सर्वर में, आप इस क्वेरी का उपयोग कर सकते हैं:

update swaptable 
set col1 = t2.col2,
col2 = t2.col1
from swaptable t2
where id = t2.id


0

मुझे केवल एक कॉलम से दूसरे (जैसे संग्रह) पर मूल्य को स्थानांतरित करना था और मूल कॉलम के मूल्य को रीसेट करना था।
नीचे (उपरोक्त उत्तर से # 3 का संदर्भ) मेरे लिए काम किया।

Update MyTable set X= (@temp:= X), X = 0, Y = @temp WHERE ID= 999;

0
CREATE TABLE Names
(
F_NAME VARCHAR(22),
L_NAME VARCHAR(22)
);

INSERT INTO Names VALUES('Ashutosh', 'Singh'),('Anshuman','Singh'),('Manu', 'Singh');

UPDATE Names N1 , Names N2 SET N1.F_NAME = N2.L_NAME , N1.L_NAME = N2.F_NAME 
WHERE N1.F_NAME = N2.F_NAME;

SELECT * FROM Names;

0

इस उदाहरण स्वैप START_DATE और END_DATE रिकॉर्ड जहां दिनांकों गलत तरीके से गोल कर रहे हैं के लिए (जब एक प्रमुख फिर से लिखने में ईटीएल प्रदर्शन कर, मैं कुछ पाया शुरुआत उनकी तुलना दिनांकों बाद अंत दिनांकों। नीचे, बुरा प्रोग्रामर!)।

सीटू में, मैं प्रदर्शन कारणों (जैसे जूलियन दिनों, लेकिन 1900-01-01 की 0 जड़ वाले) के लिए MEDIUMINTs का उपयोग कर रहा हूं, इसलिए मैं ठीक था कि WHERE mdu.start_date> mdu.end_date की एक शर्त कर रहा हूं

पीके सभी 3 कॉलमों पर व्यक्तिगत रूप से (परिचालन / अनुक्रमण कारणों से) थे।

UPDATE monitor_date mdu
INNER JOIN monitor_date mdc
    ON mdu.register_id = mdc.register_id
    AND mdu.start_date = mdc.start_date
    AND mdu.end_date = mdc.end_date
SET mdu.start_date = mdu.end_date, mdu.end_date = mdc.start_date
WHERE mdu.start_date > mdu.end_date;

FYI करें: इस कोड ने 0.203 सेकेंड में 145 / 108,456 रिकॉर्ड अपडेट किए। यह एक बंद कार्य था और इसलिए प्रदर्शन महत्वपूर्ण नहीं था।
एंड्रयू फोस्टर

0

मान लीजिए कि आप tb_user में पहले और अंतिम नाम का मान स्वैप करना चाहते हैं।

सबसे सुरक्षित होगा:

  1. Tb_user कॉपी करें। तो आपके पास 2 टेबल होंगे: tb_user और tb_user_copy
  2. UPDATE INNER JOIN क्वेरी का उपयोग करें
UPDATE tb_user a
INNER JOIN tb_user_copy b
ON a.id = b.id
SET a.first_name = b.last_name, a.last_name = b.first_name

0

आप क्वेरी के नीचे आवेदन कर सकते हैं, यह मेरे लिए एकदम सही है।

Table name: studentname
only single column available: name


update studentnames 
set names = case names 
when "Tanu" then "dipan"
when "dipan" then "Tanu"
end;

or

update studentnames 
set names = case names 
when "Tanu" then "dipan"
else "Tanu"
end;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.