MySQL - consulta UPDATE baseada na consulta SELECT

501

Preciso verificar (da mesma tabela) se há uma associação entre dois eventos com base na data e hora.

Um conjunto de dados conterá a data e hora final de determinados eventos e o outro conjunto de dados conterá a data e hora de início de outros eventos.

Se o primeiro evento for concluído antes do segundo, eu gostaria de vinculá-los.

O que tenho até agora é:

SELECT name as name_A, date-time as end_DTS, id as id_A 
FROM tableA WHERE criteria = 1


SELECT name as name_B, date-time as start_DTS, id as id_B 
FROM tableA WHERE criteria = 2

Então eu me junto a eles:

SELECT name_A, name_B, id_A, id_B, 
if(start_DTS > end_DTS,'VALID','') as validation_check
FROM tableA
LEFT JOIN tableB ON name_A = name_B

Posso, então, com base no meu campo validation_check, executar uma consulta UPDATE com o SELECT aninhado?

John M
fonte
2
Não tenho certeza do que você está perguntando? Você está tentando descobrir como fazer uma atualização com um SQL Select?
RiddlerDev 11/08/2009

Respostas:

811

Você pode fazer isso de duas maneiras:

Sintaxe de junção de atualização do MySQL:

UPDATE tableA a
INNER JOIN tableB b ON a.name_a = b.name_b
SET validation_check = if(start_dts > end_dts, 'VALID', '')
-- where clause can go here

Sintaxe ANSI SQL:

UPDATE tableA SET validation_check = 
    (SELECT if(start_DTS > end_DTS, 'VALID', '') AS validation_check
        FROM tableA
        INNER JOIN tableB ON name_A = name_B
        WHERE id_A = tableA.id_A)

Escolha o que lhe parecer mais natural.

Eric
fonte
92
O primeiro formulário (atualização com associação) será muito mais eficiente que o segundo. O primeiro faria uma junção única, enquanto o segundo executaria a consulta de seleção para cada linha da tabelaA.
#
16
Para o primeiro exemplo, você não deve usar uma junção interna? A junção esquerda não resultará em validation_check sendo definido como nulo para os registros da tabela A sem um registro correspondente da tabela B?
Cerin
3
@ Cerin sim que aconteceu comigo. Deve ser uma junção interna! Ou apenas deixe como participar. Se você não tiver uma junção interna, a junção esquerda significa que todos os registros da tabela A serão atualizados! Isso é muito perigoso!
CMCDragonkai
10
Obrigado pelo primeiro (y). Embora essa resposta tenha mais de 7 anos de idade. Não acredito por que ninguém afirmou que o segundo não funciona em alguns bancos de dados como o MySQL. Você receberá este erro:You can't specify target table ___ for update in FROM clause SQL.sql
Jugali Lakota
1
Observe também que, até recentemente, a primeira abordagem não funcionava com o MySQL quando tableA= tableB. Você é forçado a criar uma tabela temporária para armazenar o resultado intermediário. (tinha que escrever uma consulta similar para um script de migração)
svvac
301
UPDATE
    `table1` AS `dest`,
    (
        SELECT
            *
        FROM
            `table2`
        WHERE
            `id` = x
    ) AS `src`
SET
    `dest`.`col1` = `src`.`col1`
WHERE
    `dest`.`id` = x
;

Espero que funcione para voce.

massquote
fonte
5
Essa é de longe a consulta mais rápida. Edit: Tendo dest com 22K linhas e src com linhas 4K, demorou menos de 1 segundo para ser concluído, enquanto a resposta principal foi mais de 60 segundos.
Chris Dev
1
Sim, esta é a consulta mais rápida, BTW você pode adicionar cláusula WHERE também
robinmag
1
mais rápido nem sempre significa boa, esta atualização não vai funcionar em todos os casos, de fato alguns casos você terá um comportamento inesperado, tudo isso é por que não colocar uma condição e que não é uma junção
Emiliano
114

Fácil no MySQL:

UPDATE users AS U1, users AS U2 
SET U1.name_one = U2.name_colX
WHERE U2.user_id = U1.user_id
Sergio
fonte
58

Se alguém estiver tentando atualizar dados de um banco de dados para outro, não importa qual tabela esteja direcionada, deve haver alguns critérios para fazê-lo.

Este é melhor e limpo para todos os níveis:

UPDATE dbname1.content targetTable

LEFT JOIN dbname2.someothertable sourceTable ON
    targetTable.compare_field= sourceTable.compare_field
SET
    targetTable.col1  = sourceTable.cola,
    targetTable.col2 = sourceTable.colb, 
    targetTable.col3 = sourceTable.colc, 
    targetTable.col4 = sourceTable.cold 

Traaa! Funciona muito bem!

Com o entendimento acima, você pode modificar os campos definidos e os critérios "on" para fazer seu trabalho. Você também pode executar as verificações, puxar os dados para as tabelas temporárias e executar a atualização usando a sintaxe acima, substituindo os nomes de tabelas e colunas.

Espero que funcione, se não me avise. Escreverei uma consulta exata para você.

KMX
fonte
24
UPDATE 
  receipt_invoices dest,
  (
    SELECT 
      `receipt_id`,
      CAST((net * 100) / 112 AS DECIMAL (11, 2)) witoutvat 
    FROM
      receipt 
    WHERE CAST((net * 100) / 112 AS DECIMAL (11, 2)) != total 
      AND vat_percentage = 12
  ) src 
SET
  dest.price = src.witoutvat,
  dest.amount = src.witoutvat 
WHERE col_tobefixed = 1 
  AND dest.`receipt_id` = src.receipt_id ;

Espero que isso ajude você em um caso em que você deve corresponder e atualizar entre duas tabelas.

Don
fonte
12

Encontrei essa pergunta ao procurar minha própria solução para uma junção muito complexa. Esta é uma solução alternativa, para uma versão mais complexa do problema, que eu pensei que poderia ser útil.

Eu precisava preencher o campo product_id na tabela de atividades, onde as atividades são numeradas em uma unidade e as unidades são numeradas em um nível (identificado usando uma string ?? N), para que se possa identificar atividades usando um SKU, ou seja, L1U1A1. Esses SKUs são armazenados em uma tabela diferente.

Identifiquei o seguinte para obter uma lista de activity_id vs product_id: -

SELECT a.activity_id, w.product_id 
  FROM activities a 
  JOIN units USING(unit_id) 
  JOIN product_types USING(product_type_id) 
  JOIN web_products w 
    ON sku=CONCAT('L',SUBSTR(product_type_code,3), 'U',unit_index, 'A',activity_index)

Eu achei que isso era muito complexo para incorporar em um SELECT dentro do mysql, então criei uma tabela temporária e juntei-a à instrução update:

CREATE TEMPORARY TABLE activity_product_ids AS (<the above select statement>);

UPDATE activities a
  JOIN activity_product_ids b
    ON a.activity_id=b.activity_id 
  SET a.product_id=b.product_id;

Espero que alguém ache isso útil

sibaz
fonte
duas consultas podem não fornecer resultados consistentes no ambiente multiencadeado.
donald
7
UPDATE [table_name] AS T1,
      (SELECT [column_name] 
        FROM [table_name] 
        WHERE [column_name] = [value]) AS T2 
  SET T1.[column_name]=T2.[column_name] + 1
WHERE T1.[column_name] = [value];
bhavik
fonte
4

Você pode atualizar valores de outra tabela usando a junção interna como esta

UPDATE [table1_name] AS t1 INNER JOIN [table2_name] AS t2 ON t1.column1_name] = t2.[column1_name] SET t1.[column2_name] = t2.column2_name];

Siga aqui para saber como usar esta consulta http://www.voidtricks.com/mysql-inner-join-update/

ou você pode usar select como subconsulta para fazer isso

UPDATE [table_name] SET [column_name] = (SELECT [column_name] FROM [table_name] WHERE [column_name] = [value]) WHERE [column_name] = [value];

consulta explicada em detalhes aqui http://www.voidtricks.com/mysql-update-from-select/

Anand Roshan
fonte
4

Você pode usar:

UPDATE Station AS st1, StationOld AS st2
   SET st1.already_used = 1
 WHERE st1.code = st2.code
SergeyUr
fonte
3

Para a mesma tabela,

UPDATE PHA_BILL_SEGMENT AS PHA,
     (SELECT BILL_ID, COUNT(REGISTRATION_NUMBER) AS REG 
       FROM PHA_BILL_SEGMENT
        GROUP BY REGISTRATION_NUMBER, BILL_DATE, BILL_AMOUNT
        HAVING REG > 1) T
    SET PHA.BILL_DATE = PHA.BILL_DATE + 2
 WHERE PHA.BILL_ID = T.BILL_ID;
Gnanam pragasam
fonte
1

Eu tive um problema com entradas duplicadas em uma tabela em si. Abaixo estão as abordagens que estavam funcionando para mim. Também foi defendido por @sibaz.

Finalmente eu resolvi usando as consultas abaixo:

  1. A consulta de seleção é salva em uma tabela temporária

    IF OBJECT_ID(N'tempdb..#New_format_donor_temp', N'U') IS NOT NULL
        DROP TABLE #New_format_donor_temp;
    
    select *
    into #New_format_donor_temp
    from DONOR_EMPLOYMENTS
    where DONOR_ID IN (
      1, 2
    )
    
    -- Test New_format_donor_temp
    -- SELECT *
    -- FROM #New_format_donor_temp;
  2. A tabela temporária está associada à consulta de atualização.

    UPDATE de
    SET STATUS_CD=de_new.STATUS_CD, STATUS_REASON_CD=de_new.STATUS_REASON_CD, TYPE_CD=de_new.TYPE_CD
    FROM DONOR_EMPLOYMENTS AS de
      INNER JOIN #New_format_donor_temp AS de_new ON de_new.EMP_NO = de.EMP_NO
    WHERE
      de.DONOR_ID IN (
        3, 4
    )

Eu não tenho muita experiência com SQL, por favor, informe qualquer abordagem melhor que você conhece.

As perguntas acima são para o servidor MySql.

Rick
fonte