MySQL update with a subquery in safe update mode - mysql

I have the following database:
The following query calculates the total amount, that comes from multiplying the products prices and the respective quantity bought, from the orders that have the amount field equals to NULL:
SELECT SUM(i.qtde * p.preco_unitario) FROM Produtos p
INNER JOIN ItensPedido i ON i.id_produto = p.id_produto
INNER JOIN Pedidos pd ON pd.id_pedido = i.id_pedido
WHERE pd.vlr_total IS NULL
GROUP BY i.id_pedido);
I can update a row in the Pedidos table using the following query:
UPDATE Pedidos p
SET p.vlr_total = (SELECT SUM(i.qtde * p.preco_unitario) FROM Produtos p
INNER JOIN ItensPedido i ON i.id_produto = p.id_produto
WHERE i.id_pedido = 1
GROUP BY i.id_pedido)
WHERE p.id_pedido = 1;
But I have to re-run this query for each row that I want to update, changing the two WHERE clauses.
What I need help to is to build a query that updates all registries with NULL amount using a single query (if possible), similar to the second one. My attempt in using the first query as a subquery gives an 1093 error: using the same target table in update in the subquery.

You should be able to do that by changing this one line in your subquery this will set the subquery WHERE clause to match your records in the outer table.
WHERE i.id_pedido = 1
TO
WHERE i.id_pedido = p.id_pedido
Then you can remove the outer WHERE, unless you really want to just restrict it to p.id_pedido=1

Related

How to speedup my update query associated with subquery

I have a query which is pretty that contains LEFT JOIN subquery. It takes 20 minutes to load completely.
Here is my query:
UPDATE orders AS o
LEFT JOIN (
SELECT obe_order_master_id, COUNT(id) AS count_files, id, added
FROM customer_instalments
GROUP BY obe_order_master_id
) AS oci ON oci.obe_order_master_id = SUBSTRING(o.order_id, 4)
SET o.final_customer_file_id = oci.id,
o.client_work_delivered = oci.added
WHERE oci.count_files = 1
Is there any way that I can make this query runs faster?
Move Where condition in Temp Table and replace WHERE with HAVING Clause, this will eliminate unnecessary rows from temp table so reduce the filtering and may help to improve performance
UPDATE orders AS o
LEFT JOIN (
SELECT obe_order_master_id, id, added
FROM customer_instalments
GROUP BY obe_order_master_id
HAVING COUNT(id) = 1
) AS oci ON oci.obe_order_master_id = SUBSTRING(o.order_id, 4)
SET o.final_customer_file_id = oci.id,
o.client_work_delivered = oci.added
I would suggest to create separate column for Order_id substring and make index on it. Then use this column in WHERE.

retrieve only records that has join records

I have following sql query, which retrieves me joined records. However I want to get places which doesnt have joined records, see below:
SELECT `places`.* FROM `places` INNER JOIN `quotes` ON `places`.`id` = `quotes`.`place_id`
WHERE `quotes`.`user_id` = 1 AND
Here comes part I don't know how to write, but I want only to get places where count of quotes = 0 like:
"quotes.count == 0"
How to add another clause to this sql query, to match my requests?
You want an outer join, presumably a left outer join:
SELECT `places`.*
FROM `places` LEFT JOIN
`quotes`
ON `places`.`id` = `quotes`.`place_id` AND
`quotes`.`user_id` = 1
WHERE quotes.place_id is null;
Note that the condition in the where clause has been moved to the on clause. When there is no match, the columns from quotes are filled with NULLs -- which would cause a where condition to fail.

MySQL UPDATE with SELECT (mark "item" as read)

I have the following query which selects personal messages (PM) from "ac_pms" table. Additional info is fetching from other table - "ac_accounts" using LEFT JOIN. There is "pm_read" column in "ac_pms" table which define a PM is read or not. I need to set that field (pm_read) to "1" while selecting PMs.
SELECT p.*, a.seller_id, a.winner_id
FROM `ac_pms` AS p
LEFT JOIN `ac_accounts` AS a ON p.pm_for_lot = a.id
WHERE (p.pm_from=[user_id] OR p.pm_to=[user_id])
AND p.pm_for_lot=[account_id]
ORDER BY p.pm_date DESC;
I cannot imagine where to insert UPDATE expression to SET pm_read = 1.
You can't SELECT with UPDATE in the same SQL statement.
However, you can UPDATE with JOIN like this:
UPDATE ac_pms AS p
LEFT JOIN ac_accounts AS a ON p.pm_for_lot = a.id
SET p.pm_read = 1
WHERE (p.pm_from=[user_id] OR p.pm_to=[user_id])
AND p.pm_for_lot = [account_id];
Then you can make another SELECT after that.

How to fix a count() in a query with a "group by" clause?

I have a function that gets a SQL code and inserts a count field in it and executes the query to return the number of rows in it. The objective is to have a dynamic SQL code and be able to get its record count no matter what code it has, because I use it in a registry filter window and I never know what code may be generated, because the user can add as many filters as he/she wants.
But as I use the group by clause, the result is wrong because it is counting the number of times a main registry appears because of the use on many join connections.
The result of that code above should only one row with a columns with 10 as result, but I get a new table with the first columns with a 2 in the first row and a 1 on the other rows.
If I take off the group by clause I will receive a 11 as a count result, but the first row will be counted twice.
What should I do to get a single row and the correct number?
SELECT
COUNT(*) QUERYRECORDCOUNT, // this line appears only in the Count() function
ARTISTA.*,
CATEGORIA.NOME AS CATEGORIA,
ATIVIDADE.NOME AS ATIVIDADE,
LOCALIDADE.NOME AS CIDADE,
MATRICULA.NUMERO AS MAP
FROM
ARTISTA
LEFT JOIN PERFIL ON PERFIL.REGISTRO = ARTISTA.ARTISTA_ID
LEFT JOIN CATEGORIA ON CATEGORIA.CATEGORIA_ID = PERFIL.CATEGORIA
LEFT JOIN ATIVIDADE ON ATIVIDADE.ATIVIDADE_ID = PERFIL.ATIVIDADE
LEFT JOIN LOCALIDADE ON LOCALIDADE.LOCALIDADE_ID = ARTISTA.LOCAL_ATIV_CIDADE
LEFT JOIN MATRICULA ON MATRICULA.REGISTRO = ARTISTA.ARTISTA_ID
WHERE
((ARTISTA.SIT_PERFIL <> 'NORMAL') AND (ARTISTA.SIT_PERFIL <> 'PRIVADO'))
GROUP BY
ARTISTA.ARTISTA_ID
ORDER BY
ARTISTA.ARTISTA_ID;
This always gives you the number of rows for any query you have:
Select count(*) as rowcount from
(
Paste your query here
) as countquery
Since your are GROUPING BY ARTISTA.ARTISTA_ID, COUNT(*) QUERYRECORDCOUNT will return records count for each ARTISTA.ARTISTA_ID value.
If you want GLOBAL count, then you need to use a nested query:
SELECT COUNT(*) AS QUERYRECORDCOUNT
FROM (SELECT
ARTISTA.*,
CATEGORIA.NOME AS CATEGORIA,
ATIVIDADE.NOME AS ATIVIDADE,
LOCALIDADE.NOME AS CIDADE,
MATRICULA.NUMERO AS MAP
FROM
ARTISTA
LEFT JOIN PERFIL ON PERFIL.REGISTRO = ARTISTA.ARTISTA_ID
LEFT JOIN CATEGORIA ON CATEGORIA.CATEGORIA_ID = PERFIL.CATEGORIA
LEFT JOIN ATIVIDADE ON ATIVIDADE.ATIVIDADE_ID = PERFIL.ATIVIDADE
LEFT JOIN LOCALIDADE ON LOCALIDADE.LOCALIDADE_ID = ARTISTA.LOCAL_ATIV_CIDADE
LEFT JOIN MATRICULA ON MATRICULA.REGISTRO = ARTISTA.ARTISTA_ID
WHERE
((ARTISTA.SIT_PERFIL <> 'NORMAL') AND (ARTISTA.SIT_PERFIL <> 'PRIVADO'))
GROUP BY
ARTISTA.ARTISTA_ID
ORDER BY
ARTISTA.ARTISTA_ID);
In this case, you may not need to select those many columns.
If you need to retrieve the all records count with details, then better to use two separate queries.

UPDATE affecting less rows than equivalent SELECT

The following query returns 2303 rows:
SELECT a.*
FROM cur_analises a
INNER JOIN cur_materias_subsidiarias ms
ON ms.materia_id = a.materia_id
AND ms.subsidiaria_id IN(SELECT id FROM cur_subsidiarias WHERE cliente_id = 134)
INNER JOIN cur_materias m
ON m.id = a.materia_id
INNER JOIN cur_clientes c
ON c.carga_id = ms.subsidiaria_id
WHERE a.cliente_id = 134;
I need to update the cliente_id field of all those 2303 rows from table cur_analises with the value from cur_clientes.id. However, when I try to turn that SELECT query into the following UPDATE, it only affects 2297 rows, according to MySQL Workbench:
UPDATE cur_analises a
INNER JOIN cur_materias_subsidiarias ms
ON ms.materia_id = a.materia_id
AND ms.subsidiaria_id IN(SELECT id FROM cur_subsidiarias WHERE cliente_id = 134)
INNER JOIN cur_materias m
ON m.id = a.materia_id
INNER JOIN cur_clientes c
ON c.carga_id = ms.subsidiaria_id
SET a.cliente_id = c.id
WHERE a.cliente_id = 134;
I have no idea why it's missing 6 rows. What am I doing wrong?
You are probably not joining on a unique value or set of values at some point in the query causing denormalization of your result set. Then when you do the update it only updates the rows that actually meet the criteria of the joins for the table aliased as a. Only you can know what are truly non denormalizing joins in your query and fix them.
We had a similar case, the issue was with mysql partitioning. Once we revert partitioning everything is ok again.