MySQL, UPDATE one Table Joining Multiple Tables - mysql

I'm trying to update one table from another that isnĀ“t directly connected, through another table and the statement executes correctly, but it doesn't work because it's updating without data.
The query is:
UPDATE cities
INNER JOIN footprints ON cities.ID=footprints.ID
INNER JOIN (SELECT code,id_code,sum(price) AS price FROM buildings GROUP BY code,id_code) AS SUM_Price ON footprints.code=SUM_Price.code AND footprints.id_code=SUM_Price.id_code
SET cities.Total_Price=SUM_Price.price
Add some sample data as example.
Example of Data tables
Does anyone know what is happening?
Thanks for your support!

First you must join footprints to buildings and aggregate and then join to cities:
UPDATE cities c
INNER JOIN (
SELECT f.id, sum(b.price) price
FROM footprints f INNER JOIN buildings b
ON f.code = b.code AND f.id_code = b.id_code
GROUP BY f.id
) s ON s.id = c.id
SET c.total_price = s.price

Related

How can I view all values that my update query has changed? in mysql

Hey guys i have a new problem with my sql...
i try to update all articles that are assigned to a certain category in my shopware shop.
i built a query for it:
update s_articles_prices
INNER JOIN s_articles on s_articles_prices.articleID=s_articles.id
INNER JOIN s_articles_supplier on s_articles.supplierID=s_articles_supplier.id
INNER JOIN s_articles_details on s_articles.id=s_articles_details.articleID
SET price = price + 10
WHERE s_articles_supplier.name="category name"
now i would like to know, which values my query has updated...
I have a select query too, can i connect these two querys to show exactly that tables they i have ben updatded with my update query?
Here is my select:
SELECT s_articles.id as aid, s_articles.name as aName, s_articles.supplierID as sID, s_articles_prices.pricegroup as pricegroup, s_articles_prices.price as price, s_articles_supplier.name as sName, s_articles_details.ordernumber as sku
FROM s_articles
INNER JOIN s_articles_prices on s_articles.id=s_articles_prices.articleID
INNER JOIN s_articles_supplier on s_articles.supplierID=s_articles_supplier.id
INNER JOIN s_articles_details on s_articles.id=s_articles_details.articleID
WHERE s_articles_supplier.name="category name"
I would like to have a similar output as the select now, so that i can check at a glance if my update has worked
Thank you for your time :)
With the help of DiarSelimi and RaymondNijland I was able to solve my problem.
I have now done it this way:
I have create a sql script with this parts:
create a temporary table:
CREATE TEMPORARY TABLE snapTable
SELECT s_articles.id as aid, s_articles.name as aName, s_articles.supplierID as sID, s_articles_prices.pricegroup as pricegroup, s_articles_prices.price as price, s_articles_supplier.name as sName, s_articles_details.ordernumber as sku
FROM s_articles
INNER JOIN s_articles_prices on s_articles.id=s_articles_prices.articleID
INNER JOIN s_articles_supplier on s_articles.supplierID=s_articles_supplier.id
INNER JOIN s_articles_details on s_articles.id=s_articles_details.articleID
WHERE s_articles_supplier.name="category name";
run my update:
update s_articles_prices
INNER JOIN s_articles on s_articles_prices.articleID=s_articles.id
INNER JOIN s_articles_supplier on s_articles.supplierID=s_articles_supplier.id
INNER JOIN s_articles_details on s_articles.id=s_articles_details.articleID
SET price = price + 10
WHERE s_articles_supplier.name="category name";
and compare my temp table with the live table:
SELECT aid, sku, snapTable.price, s_articles_prices.price as newPrice
FROM snapTable
INNER JOIN s_articles on snapTable.aid=s_articles.id
INNER JOIN s_articles_prices on snapTable.aid=s_articles_prices.articleID;
and you'll have something like that:
aid|sku |price |newPrice
-------------------------------------------------
2 |number |202.60504201681 |212.60504201681
2 |number |202.60504201681 |212.26050420167996
maybe it will help someone else too :)

join nested queries in from clause

Can you tell me what I need to manipulate in this query to get it working?
select C.ID from
(select A.ID from CUSTOMERS A inner join PROFILES B on A.ID=B.ID where CTR='67564' and CST_CD in
('G','H')) as C
inner join
(select ID from RELATION_CODES where R_CD='KC') as R
on C.ID=R.ID
The individual inner queries are working just fine and giving correct results, not sure what is the problem with inner join in from clause..
Not completely sure I'm understanding your question, but this should be able to be rewritten without the subqueries:
select c.id
from customers c
join profiles p on c.id = p.id
join relation_codes rc on rc.id = c.id
where ctr = '67564'
and cst_cd in ('G','H')
and rc.r_cd = 'KC'
If this isn't working, please provide your table structure and sample data and expected results. This should get you pretty close though.
I have to ask, is the id field in the relation_codes table and the profiles table the same as the id in the customers table. Perhaps you need to identify how your tables are related.
A Visual Explanation of SQL Joins

Incomprehensible query behaviour

I have multiple tables, related by multiple foreign keys as in the following example:
Recipes(id_recipe,name,calories,category) - id_recipe as PK.
Ingredients(id_ingredient,name,type) - id_ingredient as PK.
Contains(id_ingredient,id_recipe,quantity,unit) - (id_ingredient,id_recipe) as PK, and as Foreign Keys for Recipes(id_recipe) and Ingredients(id_ingredient).
You can see this relations represented in this image.
So basically Contains is a bridge between Recipes and Ingredients.
The query I try to write it's supposed to give as result the names of the recipes whose ingredients type are "bovine" but not "lactic".
My attempt:
SELECT DISTINCT Recipes.name
FROM Ingredients JOIN Contains USING(id_ingredient) JOIN Recipes USING (id_recipe)
WHERE Ingredients.type = "bovin"
AND Ingredients.type <> "lactic";
The problem is it still shows me recipes that have at least one lactic ingredient.
I would appreciate any help!
This is the general form of the kind of query you need:
SELECT *
FROM tableA
WHERE tableA.ID NOT IN (
SELECT table_ID
FROM ...
)
;
-- EXAMPLE BELOW --
The subquery gives the id values of all recipes that the "lactic" ingredient is used in, the outer query says "give me all the recipes not in that list".
SELECT DISTINCT Recipes.name
FROM Recipes
WHERE id_recipe IN (
SELECT DISTINCT id_recipe
FROM `Ingredients` AS `i`
INNER JOIN `Contains` AS `c` USING (id_ingredient)
WHERE `i`.`type` = "lactic"
)
;
Alternatively, using your original query:
You could've changed the second join to a LEFT JOIN, changed it's USING to an ON & included AND type = "lactic" there instead, and ended the query with HAVING Ingredients.type IS NULL (or WHERE, I just prefer HAVING for "final result" filtering). This would tell you which items could not be joined to the "lactic" ingredient.
A common solution of this type of question (checking conditions over a set of rows) utilizes aggregate + CASE.
SELECT R.Name
FROM Recipes R
INNER JOIN Contains C
on R.ID_Recipe = C.ID_Recipe
INNER JOIN Ingredients I
on C.ID_Ingredient = I.ID_Ingredient
GROUP BY R.name
having -- at least one 'lactic' ingredient
sum(case when type = 'lactic' then 1 else 0 end) = 0
and -- no 'bovin' ingredient
sum(case when type = 'bovin' then 1 else 0 end) > 0
It's easy to extend to any number of ingredients and any kind of question.
Hijacked the fiddle of xQbert
SELECT R.NAME
FROM CONTAINS C
INNER JOIN INGREDIENTS I
ON I.ID_INGREDIENTS = C.ID_INGREDIENTS AND I.TYPE = 'bovine' AND I.TYPE <> "lactic"
INNER JOIN RECIPES R
ON R.ID_RECIPE = C.ID_RECIPE
GROUP BY R.NAME
That should work, maybe you need to escape 'contains'. It could be recognized as a SQL function.
SQL Fiddle
In my example burgers and pasta have 'Bovin' and thus show up. So do cookies but cookies also have 'lactic' which is why they get excluded.
SELECT R.Name
FROM Recipes R
INNER JOIN Contains C
on R.ID_Recipe = C.ID_Recipe
INNER JOIN Ingredients I
on C.ID_Ingredient = I.ID_Ingredient
LEFT JOIN (SELECT R2.ID_Recipe
FROM Ingredients I2
INNER JOIN Contains C2
on C2.ID_Ingredient = I2.ID_Ingredient
INNER JOIN Recipes R2
on R2.ID_Recipe = C2.ID_Recipe
WHERE Type = 'lactic'
GROUP BY R2.ID_Recipe) T3
on T3.ID_Recipe = R.ID_Recipe
WHERE T3.ID_Recipe is null
and I.Type = 'Bovin'
GROUP BY R.name
There likely is a more elegant way of doing this. I really wanted to CTE this and join it to itself.. but no CTE in mySQL. Likely a way to do this using exists too.... I'm not a big fan of using IN clauses as the performance generally suffers. Exists fastest, Joins 2nd fastest, in slowest (generally speaking)
The inline view (sub query) returns the ID_recipe of those you don't want to include.
The outer query returns the Name of the recipes with ingredients you want.
By joining these two together using an outer join we return all recipes and only those with the undesired ingredient. We then limit the results to only those where the recipe ID doesn't exist for the undesired ingredient. (undesired ingredient not found) you'll get only those recipes having all desired ingredients.
You can use NOT EXISTS for this.
Try this:
SELECT DISTINCT Recipes.`name`
FROM Recipes JOIN Contains AS C1 USING (id_recipe) JOIN Ingredients USING(id_ingredient)
WHERE Ingredients.type = "bovin"
AND NOT EXISTS (
SELECT 1
FROM Contains AS C2 JOIN Ingredients USING(id_ingredient)
WHERE C1.id_recipe = C2.id_recipe
AND Ingredients.type = "lactic"
)

mysql subquery inside a LEFT JOIN

I have a query that needs the most recent record from a secondary table called tbl_emails_sent.
That table holds all the emails sent to clients. And most clients have several to hundreds of emails recorded. I want to pull a query that displays the most recent.
Example:
SELECT c.name, c.email, e.datesent
FROM `tbl_customers` c
LEFT JOIN `tbl_emails_sent` e ON c.customerid = e.customerid
I'm guessing a LEFT JOIN with a subquery would be used, but I don't delve into subqueries much. Am I going the right direction?
Currently the query above isn't optimized for specifying the most recent record in the table, so I need a little assistance.
It should be like this, you need to have a separate query to get the maximum date (or the latest date) that the email was sent.
SELECT a.*, b.*
FROM tbl_customers a
INNER JOIN tbl_emails_sent b
ON a.customerid = b.customerid
INNER JOIN
(
SELECT customerid, MAX(datesent) maxSent
FROM tbl_emails_sent
GROUP BY customerid
) c ON c.customerid = b.customerid AND
c.maxSent = b.datesent
Would this not work?
SELECT t1.datesent,t1.customerid,t2.email,t2.name
FROM
(SELECT max(datesent) AS datesent,customerid
FROM `tbl_emails_sent`
) as t1
INNER JOIN `tbl_customers` as t2
ON t1.customerid=t2.customerid
Only issue you have then is what if two datesents are the same, what is the deciding factor in which one gets picked?

Left outer join to a generated table?

Am I on completely the wrong tack ?
I want to do a left outer join to a query generated from 2 tables , but i keep getting errors. Do I need a different approach?
t1:
ID, Surname,Firstname
t2:
ID,JobNo,Confirmed
I have the following query:
SELECT JobNo AS N, StaffID AS P, Confirmed as C,
FirstName AS F,Surname AS S
FROM gigs_players, Players
WHERE t1.StaffID=t2.StaffID AND JobNo="2"
AND (`Confirmed` IS NULL OR Confirmed ='Y' )
ORDER BY Instrument,Surname
I want to add:
LEFT OUTER JOIN contacted (ON t1.StaffID=contact.ID AND t2.JobNo=contact.JobNo)"
Can I do a left outer join to a query generated from 2 tables ?
In order to use the t1 and t2 in the left outer join that you want to add you need to join them with the first tables, you can't reference them directly in the left outer join you, Something like the following:
SELECT JobNo AS N, StaffID AS P, Confirmed as C,
FirstName AS F,Surname AS S
FROM gigs_players, Players
Inner join t1 on ...
Inner join t2 on ...
LEFT OUTER JOIN contacted c
on t1.StaffID=c.ID AND t2.JobNo = c.JobNo
WHERE t1.StaffID=t2.StaffID AND JobNo="2"
AND (`Confirmed` IS NULL OR Confirmed ='Y' )
ORDER BY Instrument,Surname
So, based in your tables' structure, define the conditions of the two joins with t1 and t2 with other tables.
Here is the an example of a left join to a sub query. This might be what you are looking for.
select
parts.id,
min(inv2.id) as nextFIFOitemid
from test.parts
left join
( select
inventory.id,
coalesce(parts.id, 1) as partid
from test.inventory
left join test.parts
on (parts.id = inventory.partid)
) inv2
on (parts.id = inv2.partid)
group by parts.id;