Przełączanie wartości w kolumnie za pomocą jednej instrukcji aktualizacji

12

Okazuje się, że błąd w systemie nieprawidłowo nazwał mężczyzn (M) kobietami (W) i odwrotnie w bazie danych. Kolumny dopuszczają tylko jeden znak. Bez użycia tabel tymczasowych napisz jedno zapytanie o aktualizację, aby rozwiązać ten problem.

To pytanie zostało zadane podczas ostatniego wywiadu, który przeprowadziłem, i przechodzę do kolejnych wywiadów, które mogą mieć podobne pytania, więc chciałem dowiedzieć się, jak sobie z tym poradzić.

Seth Tak
źródło
6
Czy poproszono Cię o przyjęcie konkretnego produktu bazodanowego? np. MySQL, SQL Server, Oracle, PostgreSQL ...?
Paul White 9
Czy Twój system przeczytał nowe wytyczne dla społeczności? : \
AER

Odpowiedzi:

23

Chcesz użyć CASEwyrażenia pewnego typu.

W SQL Server kod wygląda następująco:

UPDATE TableName
SET gender = CASE WHEN gender = 'M' THEN 'W' 
                  WHEN gender = 'W' THEN 'M'
                  ELSE gender END

Edycja: Jak stwierdzono w komentarzach (i niektórych innych odpowiedziach), ELSE nie jest konieczne, jeśli umieścisz w instrukcji klauzulę WHERE.

UPDATE TableName
SET gender = CASE WHEN gender = 'M' THEN 'W' 
                  WHEN gender = 'W' THEN 'M' END
WHERE gender IN ('M','W')

Pozwala to uniknąć niepotrzebnych aktualizacji. W obu przypadkach ważne jest, aby pamiętać, że istnieją opcje inne niż M & W (na przykład NULL) i nie chcesz wprowadzać błędnych informacji. Na przykład:

UPDATE TableName
SET gender = CASE WHEN gender = 'M' THEN 'W' 
                  ELSE 'M' END

Zastąpiłoby to wszystkie wartości NULL (lub inne możliwe płcie) jako „M”, co byłoby niepoprawne.


Byłoby kilka innych opcji

/*Simple form of CASE rather than Searched form*/
UPDATE TableName
SET    gender = CASE gender
                  WHEN 'M' THEN 'W'
                  WHEN 'W' THEN 'M'
                END
WHERE  gender IN ( 'M', 'W' );

I bardziej zwięzłe

/*For SQL Server 2012+*/
UPDATE TableName
SET    gender = IIF(gender = 'M', 'W', 'M')
WHERE  gender IN ( 'M', 'W' ); 
Kenneth Fisher
źródło
1
Można wymienić IIF()ze IF()i to działa w MySQL;)
ypercubeᵀᴹ
9

W Oracle możesz użyć CASE, ponieważ inne odpowiedzi mają:

UPDATE TableName
SET gender = CASE WHEN gender = 'M' THEN 'W' 
                  WHEN gender = 'W' THEN 'M'
             END
WHERE gender in ('M','W');

Możesz także użyć DECODE:

UPDATE TableName SET gender = DECODE(gender,'M','W','W','M')
WHERE gender in ('M','W');
Leigh Riffel
źródło
5

Aby przełączać się między dwiema wartościami, możesz także wypróbować tę sztuczkę, która nie używa CASEwyrażenia (zakładając tutaj Transact-SQL):

UPDATE
  YourTable
SET
  Gender = CHAR(ASCII('M') + ASCII('W') - ASCII(Gender))
WHERE
  Gender IN ('M', 'W')
;

W zależności od bieżącej wartości Gender, ASCII(Gender)anuluje albo, ASCII('M')albo ASCII('W')pozostawiając drugi kod do przekształcenia przez CHAR()funkcję z powrotem na odpowiedni znak.

Ale pozostawiam to tylko dla porównania. Chociaż ta opcja może mieć pozory elegancji, rozwiązanie wykorzystujące CASEwyrażenie byłoby prawdopodobnie bardziej czytelne, a zatem łatwiejsze w utrzymaniu, i zdecydowanie łatwiej byłoby rozszerzyć je na więcej niż dwie wartości.

Andriy M.
źródło
2
Miejmy nadzieję, że wszystkie Mi Wzostały wprowadzone dużymi literami, aby uniknąć nieoczekiwanego 7lub pojawienia się `-` w wynikach.
Martin Smith
@MartinSmith: Bardzo dobry punkt. Jeśli nie były, będziemy musieli zastąpić ASCII(Gender)je ASCII(UPPER(Gender)), co jest mniej eleganckie, choć niewiele.
Andriy M
@MartinSmith, jeśli są małe litery m i w, czy nie zostaną odrzucone przez WHEREklauzulę?
ypercubeᵀᴹ
1
@ YperSillyCubeᵀᴹ - tylko w przypadku sortowania wrażliwe (które nie są zwykle IME)
Martin Smith
4

Możesz to zrobić za pomocą case ... whenwyrażenia:

mysql> select * from genderswap;
+--------+
| gender |
+--------+
| F      |
| F      |
| M      |
| M      |
| M      |
| M      |
| M      |
+--------+
7 rows in set (0.00 sec)

mysql> 
mysql> UPDATE genderswap SET gender = case 
    ->                                when gender='M' then 'F' 
    ->                                when gender='F' then 'M'
    ->                                end
    -> WHERE gender IN ('M', 'F');
Query OK, 7 rows affected (0.00 sec)
Rows matched: 7  Changed: 7  Warnings: 0

mysql> 
mysql> select * from genderswap;
+--------+
| gender |
+--------+
| M      |
| M      |
| F      |
| F      |
| F      |
| F      |
| F      |
+--------+
7 rows in set (0.00 sec)

mysql> 
Philᵀᴹ
źródło
2

Użyłbym aktualizacji z casewyrażeniem.

DECLARE @Test TABLE
    (
      Name VARCHAR(100) NULL
    , Gender CHAR(1) NULL
    );

INSERT  INTO @Test
        ( Name, Gender )
VALUES  ( 'Jonathan', 'W' )
         ,
        ( 'Kelly', 'M' );

SELECT  Name
      , Gender
FROM    @Test;

UPDATE  @Test
SET     Gender = CASE WHEN Gender = 'M' THEN 'W'
                      ELSE 'M'
                 END;

SELECT  Name
      , Gender
FROM    @Test;
Jonathan Fite
źródło
-1

Możesz wykonać tę aktualizację za pomocą casewyrażenia.

UPDATE names_table
   SET names_table.gender = ( CASE
                                  WHEN names_table.gender = 'M'
                                    THEN 'W'
                                  ELSE
                                      names_table.gender = 'M'
                              END)

Sugeruję uruchomienie instrukcji aktualizacji w ramach transakcji i dodanie prostego zapytania, takiego jak:

SELECT n.gender, *
FROM names_table

w celu sprawdzenia wyników, które otrzymasz. Przeprowadzanie transakcji z wycofaniem i przełączanie jej na zatwierdzenie, gdy wyniki są zgodne z oczekiwaniami.

agpoweredmg
źródło