MySQL

2008-09-01 7 views
92

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

क्या यह अद्यतन से कुछ तरीके से करना संभव है? अद्यतन तालिका सेट एक्स = वाई, वाई = एक्स जाहिर है जो मैं चाहता हूं वह नहीं करेगा।


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

+1

नोट के रूप में, 'अद्यतन तालिका सेट एक्स = वाई, वाई = एक्स' एसक्यूएल में इसे करने का मानक तरीका है, केवल MySQL misbehaves। –

उत्तर

135

I just had to deal with the same and I'll summarize my findings.

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

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

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

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

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

अनिवार्य रूप से, 1 टेबल एक अद्यतन हो रही है और 2 से एक के लिए प्रयोग किया जाता है:

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

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

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

    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); 
    
  • +15

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

    +0

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

    +5

    आप जानते हैं, मैंने कभी नहीं सोचा था कि उस बेवकूफ साक्षात्कार प्रश्न के लिए व्यावहारिक उपयोग होगा, अस्थायी उपयोग किए बिना दो चरों को स्वैप करने के लिए कहा जा रहा है, लेकिन यहां यह है, और पूर्णांक के लिए यह वास्तव में काम करेगा: अद्यतन swap_test set x = x + y , y = xy, एक्स = xy; – 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; 
    
    Something like this?

    Edit: About Greg's comment: No, this doesn't work:

     
    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)

    +0

    सिर्फ रिकॉर्ड के लिए: यह * करता है * PostgreSQL में काम करता है जबकि यह * My *QL में काम नहीं करता है। – str

    8

    UPDATE table SET X=Y, Y=X will do precisely what you want (edit: in PostgreSQL, not MySQL, see below). The values are taken from the old row and assigned to a new copy of the same row, then the old row is replaced. You do not have to resort to using a temporary table, a temporary column, or other swap tricks.

    @D4V360: I see. That is shocking and unexpected. I use PostgreSQL and my answer works correctly there (I tried it). See the PostgreSQL UPDATE docs (under Parameters, expression), where it mentions that expressions on the right hand side of SET clauses explicitly use the old values of columns. I see that the corresponding MySQL UPDATE docs contain the statement "Single-table UPDATE assignments are generally evaluated from left to right" which implies the behaviour you describe.

    Good to know.

    +0

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

    +0

    "x = y, y = x" दृष्टिकोण ओरेकल में भी काम करता है, इसके लिए यह क्या लायक है। –

    +2

    मैंने पोस्टग्रेएसक्यूएल और एसईटी एक्स = वाई का उपयोग किया, वाई = एक्स ने मुझे बचाया :) – Anonymous

    5

    Ok, so just for fun, you could do this! (assuming you're swapping string values)

    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) 
    

    A nice bit of fun abusing the left-to-right evaluation process in MySQL.

    Alternatively, just use XOR if they're numbers. You mentioned coordinates, so do you have lovely integer values, or complex strings?

    Edit: The XOR stuff works like this by the way:

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

    Assuming you have signed integers in your columns, you may need to use CAST(a^b AS SIGNED), since the result of the^operator is an unsigned 64-bit integer in MySQL.

    In case it helps anyone, here's the method I used to swap the same column between two given rows:

    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 
    

    where $1 and $2 are the keys of two rows and $3 is the result of the first query.

    2

    This surely works! I've just needed it to swap Euro and SKK price columns. :)

    UPDATE tbl SET X=Y, [email protected] where @temp:=X; 
    

    The above will not work (ERROR 1064 (42000): You have an error in your SQL syntax)

    18

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

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

    'अद्यतन तालिका swap_test'? क्या यह 'swap_test अद्यतन' नहीं होना चाहिए? – Pang

    1

    आप परिवर्तन स्तंभ नाम कर सकता है, लेकिन यह एक अधिक है हैक।लेकिन किसी भी अनुक्रमित कि ये कॉलम

    30

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

    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> 
    

    किया जा रहा है दे दो यह एक प्रयास करें है! !!

    +3

    संख्याओं के लिए यह वास्तव में एक सबसे अच्छा है। –

    +0

    @YourCommonSense आपका बक्षीस रोनाल्डो कहता है जब यह वास्तव में रोलांडो है। – Albzi

    0

    का उपयोग कर स्तंभ मानों की गमागमन एकल क्वेरी

    अद्यतन My_table सेट एक = @ tmp: = एक, a = b, ख = @ tmp;

    चीयर्स ...!

    +0

    यह [स्वीकृत उत्तर] के # 3 का दोहराव है (http://stackoverflow.com/a/559291/1402846)। – Pang

    4

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

    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 या शून्य या शून्य लंबाई स्ट्रिंग या सिर्फ रिक्त स्थान है। (संग्रहण) की तरह और रीसेट

    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) 
    
    +0

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

    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

    मैं सिर्फ दूसरे के स्तंभ से मान ले जाने के लिए किया था:

    हम

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

    यहाँ के लिए इसे बदल की जरूरत स्क्रिप्ट है मूल कॉलम का मूल्य।
    नीचे (ऊपर स्वीकृत उत्तर से # 3 का संदर्भ) मेरे लिए काम किया।

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