MySQL DELETE with JOIN on condition - mysql

I have 3 tables; products, variants, and stock {simplified}
PRODUCTS
id
name
discontinued (ENUM: 0 or 1)
etc
VARIANTS
id
product_id
colour
size
STOCK
id
variant_id
branch_id
When the user selects to discontinue the PRODUCT I set the discontinued flag to 1. That's fine, but I want to delete the PRODUCT and VARIANTS records altogether if there is no STOCK record of the product. Obviously I can do this using a SELECT query first in PHP but I would like to do it in one mySQL query. This is what I have so far, but it is returning an error from mySQL:
$query = "DELETE FROM prod_lines,
JOIN variants ON variants.lineid = prod_lines.id
WHERE prod_lines.id = '$lineid' AND
(SELECT COUNT(id) FROM stock WHERE stock.variantid = variants.id) = 0";
Can anybody out there help me come up with the right solution? Maybe there is a better way that doesn't even involve a subquery?
Thanks in advance

I assume that the product_line table in your query is the PRODUCTS table you describe in the beginning of your post.
DELETE prod_lines.* -- you must specify which table you are deleting from, because there are several tables in the FROM clause
FROM prod_lines
JOIN variants ON variants.lineid = prod_lines.id
LEFT JOIN stock ON stock.variantid = variants.id
WHERE prod_lines.id = #lineid -- your "$lineid" variable here
AND stock.id IS NULL; -- selects only items with no match in the "stock" table
http://sqlfiddle.com/#!2/40a21

Try something like this,the downside is though you have to have two php variables here and also an nested query that you may not be ok with
DELETE FROM (select COUNT(id) countstock FROM stock s,variants v WHERE s.variantid = v.id and v.productid='$lineid') prod
where prod.countstock>1 and prod.productid='$lineid'

Related

Replace a column in another table in mysql with a condition

I want to replace tags rows in my database news table product where the product name is equal to the product name in another table product.
I want to copy the tags column from the products database to the product database tags must be in the correct row like the name column must match both tables.
I tried a query but it send me an error:
Table to copy = product
Table from copy products
Condition (name=name) or (slug=slug)
In both Table:
UPDATE product
set tags = (
select tags
FROM products
where product.name = products.name
);
It gives an error:
Subquery returns more than 1 row
It works with:
UPDATE product
set tags = (
select tags
FROM products
where product.id = products.id
);
What I have found to better understand and make sure you ultimately want in the final update, yet also make sure you are getting the records intended. Write the select first.
select
np.name,
np.tags NowTags,
op.tags OtherTags
from
NowProduct np
JOIN OtherProduct op
on np.Name = op.Name
Once you have this confirmed, just change the SELECT to an UPDATE. You can use the "alias" for the table being updated. I found its best to make sure your query is correct to see what records WILL be queried and what the before and what you WANT updated values should be. Once good, then apply update.
update np set
tags = op.tags
from
NowProduct np
JOIN OtherProduct op
on np.Name = op.Name

Compare differences in 2 tables

I am running a MySQL Server on Ubuntu, patched up to date...
In MySQL, I have 2 tables in a database. I am trying to get a stock query change working and it kind of is, but it's not :(
What I have is a table (table A) that holds the last time I have checked stock levels, and another table (table B) that holds current stock levels. Each table has identical column names and types.
What I want to do is report on the changes from table B. The reason is that there are about 1/2 million items in this table - and I cannot just update each item using the table as a source as I am limited to 100 changes at a time. So, ideally, I want to get the changes - store them in a temporary table, and use that table to update our system with just those changes...
The following below brings back the changes but shows both Table A and Table B.
I have tried using a Left Join to only report back on Table B but I'm not a mysql (or any SQL) guy, and googling all this... Can anyone help please. TIA. Stuart
SELECT StockItemName,StockLevel
FROM (
SELECT StockItemName,StockLevel FROM stock
UNION ALL
SELECT StockItemName,StockLevel FROM stock_copy
) tbl
GROUP BY StockItemName,StockLevel
HAVING count(*) = 1
ORDER BY StockItemName;
The query below spit out records that have different stock level in both table.
SELECT s.StockItemName, s.StockLevel, sc.StockLevel
FROM stock s
LEFT JOIN stock_copy sc ON sc.Id = s.Id AND sc.StockLevel <> s.StockLevel
ORDER BY s.StockItemName
ok - I solved it - as there wasn't a unique ID on each table that could be matched, and rather than make one, I used 3 colums to create the unique ID and left joined on that.
SELECT sc.StockItem, sc.StockItemName, sc.Warehouse, sc.stocklevel
FROM stock s
LEFT JOIN stock_copy sc ON (sc.StockItem = s.StockItem AND sc.StockItemName = s.StockItemName AND sc.Warehouse = s.Warehouse AND sc.StockLevel <> s.StockLevel)
having sc.StockLevel is not Null;

MySQL error updating a table using join

I am trying to update a table in a mysql database, and am getting a syntax error. It is a MyISAM table if that matters.
Here is the sql
UPDATE product SET price=(price*1.0909)
JOIN product_to_category ON product.product_id = product_to_category.product_id
WHERE category_id =6
OR category_id =1
OR category_id =2
My goal is to get a list of products from 3 specific categories (information from the *product_to_category* table) and increase the price by about 10%. The price is contained in the product table.
From what I see in the documentation I can use join in the update statement, and I have done similar queries in the past.
This is a production website, which currently has about 40,000 products. If needed I can do a php script that will loop through the products and do it one by one, but it seems like I should be able to do it directly from mysql.
Your statement is a little bit messed up. SET follows after JOIN which is part of the UPDATE clause.
UPDATE product
JOIN product_to_category
ON product.product_id = product_to_category.product_id
SET price = price * 1.0909
WHERE category_id IN (1,2,6)

Parse & Compare Data using Coldfusion & MySQL

First, I'll explain what I need to do, then how I think I can achieve it. My current plan seems very inefficient in theory, so my question is whether there is a better way of accomplishing it.
I have 2 Tables - lets call them 'Products' and 'Products_Temp', both are identical. I need to download a large number of files (XML or XLS) which contain product details (stock, pricing etc) from suppliers. These are then parsed into the Products_Temp table. Right now, I plan to use CF Scheduled Tasks to handle the downloading, and Navicat to do the actual parsing - I'm happy enough this is adequate and efficient enough.
The next step is where I'm struggling - once the file has been downloaded and parsed, I need to look for any changes in the data. This will be compared against the Products table. If a change is found, then that row should be added or updated (if it should be removed, then I'll need to flag it rather than just delete it). Once all the data has been compared, the products_temp table should be emptied.
I'm aware of methods to compare tables and sync them accordingly, however the issue I have is the fact I'll be handling multiple files from different sources. I had considered using only the products table and append/update, but I'm unsure how I could manage the 'flag deleted' requirement.
Right now, the only way I know I can make it work is to loop through the products_temp table, do various cfquerys and delete the row once complete. However, that seems incredibly inefficient, and given the fact we're likely to be dealing with hundreds of thousands of rows, unlikely to be effective if we update everything daily.
Any pointers or advice on a better route would be appreciated!
Both responses have possibilities. Just to expand on your options a little ..
Option #1
IF mySQL supports some sort of hashing, on a per row basis, you could use a variation of comodoro's suggestion to avoid hard deletes.
Identify Changed
To identify changes, do an inner join on the primary key and check the hash values. If they are different, the product was changed and should be updated:
UPDATE Products p INNER JOIN Products_Temp tmp ON tmp.ProductID = p.ProductID
SET p.ProductName = tmp.ProductName
, p.Stock = tmp.Stock
, ...
, p.DateLastChanged = now()
, p.IsDiscontinued = 0
WHERE tmp.TheRowHash <> p.TheRowHash
Identify Deleted
Use a simple outer join to identify records that do not exist in the temp table, and flag them as "deleted"
UPDATE Products p LEFT JOIN Products_Temp tmp ON tmp.ProductID = p.ProductID
SET p.DateLastChanged = now()
, p.IsDiscontinued = 1
WHERE tmp.ProductID IS NULL
Identify New
Finally, use a similar outer join to insert any "new" products.
INSERT INTO Products ( ProductName, Stock, DateLastChanged, IsDiscontinued, .. )
SELECT tmp.ProductName, tmp.Stock, now() AS DateLastChanged, 0 AS IsDiscontinued, ...
FROM Products_Temp tmp LEFT JOIN Products p ON tmp.ProductID = p.ProductID
WHERE p.ProductID IS NULL
Option #2
If per row hashing is not feasible, an alternate approach is a variation of Sharondio's suggestion.
Add a "status" column to the temp table and flag all imported records as "new", "changed" or "unchanged" through a series of joins. (The default should be "changed").
Identify UN-Changed
First use an inner join, on all fields, to identify products that have NOT changed. (Note, if your table contains any nullable fields, remember to use something like coalesce Otherwise, the results may be skewed because null values are not equal to anything.
UPDATE Products_Temp tmp INNER JOIN Products p ON tmp.ProductID = p.ProductID
SET tmp.Status = 'Unchanged'
WHERE p.ProductName = tmp.ProductName
AND p.Stock = tmp.Stock
...
Identify New
Like before, use an outer join to identify "new" records.
UPDATE Products_Temp tmp LEFT JOIN Products p ON tmp.ProductID = p.ProductID
SET tmp.Status = 'New'
WHERE p.ProductID IS NULL
By process of elimination, all other records in the temp table are "changed". Once you have calculated the statuses, you can update the Products table:
/* update changed products */
UPDATE Products p INNER JOIN Products_Temp tmp ON tmp.ProductID = p.ProductID
SET p.ProductName = tmp.ProductName
, p.Stock = tmp.Stock
, ...
, p.DateLastChanged = now()
, p.IsDiscontinued = 0
WHERE tmp.status = 'Changed'
/* insert new products */
INSERT INTO Products ( ProductName, Stock, DateLastChanged, IsDiscontinued, .. )
SELECT tmp.ProductName, tmp.Stock, now() AS DateLastChanged, 0 AS IsDiscontinued, ...
FROM Products_Temp tmp
WHERE tmp.Status = 'New'
/* flag deleted records */
UPDATE Products p LEFT JOIN Products_Temp tmp ON tmp.ProductID = p.ProductID
SET p.DateLastChanged = now()
, p.IsDiscontinued = 1
WHERE tmp.ProductID IS NULL
For finding the changes, I'd look at joins based on the fields you want to match on. This can be slow, depending on the number of fields and whether or not they're indexed, but I'd still say it was faster than loops. Something along the lines of:
SELECT product_id
FROM Products
WHERE product_id NOT IN (
SELECT T.product_id
FROM Products_Temp T
INNER JOIN PRODUCTS P
ON (
P.field1 = T.field1
AND P.field2 = T.field2
...
)
)
For the missing products to find the non-matches:
SELECT P.product_id
FROM Products P
LEFT OUTER JOIN Products_Temp T
ON (P.field1 = T.field1
AND P.field2 = T.field2
...)
WHERE T.product_id IS NULL
I had to solve a similar problem once, maybe the solution is applicable in your case (I do not know Coldfusion much). Why not (for each source) just delete everything from table Products corresponding to that source and replacing it with Products_Temp from the same source? It assumes you can make a unique field for each source. The SQL code would look something like:
DELETE FROM Products WHERE source_id = x;
INSERT INTO Products (field1, field2, ..., source_id)
SELECT field1, field2, ..., x FROM Products_Temp;
Also if the source doesn't change much, you can consider making a hash after its downloading and skipping the update if it did not change to save some database access.

Mysql Query - Joins causing confusion for me in this query

I have a query that returns results related to items that match a specific category...
There are 3 mysql tables that results to this, items, categories and item_categories.
These i assume are self explanatory, but the latter, is a linking table that links any specific item to any specific category, using a match of id's.
The items table contains one row, with an id value of 1.
The categories table is filled with 15 rows, with id values of 1-15.
the item_categories table contains one row, the item_id value is 1 and the category_id value is 5.
This is the mysql query in its php form:
$catResultQuery = "
SELECT i.id, name, price
FROM items i
INNER JOIN item_categories
ON i.id = item_id
INNER JOIN categories c
ON category_id = c.id
WHERE MATCH (c.id)
AGAINST ('{$_SESSION['input']}' IN BOOLEAN MODE)
ORDER BY name
";
The session variable has a value of 5, but for some reason, this query displays a 0 result set.
Even when i run the query in php myadmin, it returns 0 rows.
And i am confused, because in my head, the logic behind all of this seems fairly simple, but for some reason i get 0? Does anyone have any idea where i have gone wrong with this?
Any advice and input would be greatly appreciated, thank you!!
Ok, I see now that you're building the SQL dynamically. If that's the case, then this should work:
SELECT i.id, name, price
FROM items i
INNER JOIN item_categories
ON i.id = item_id
INNER JOIN categories c
ON category_id = c.id
WHERE c.id
IN ('{$_SESSION['input']}')
ORDER BY name
Just make sure '{$_SESSION['input']}' is comma delimited and be aware that this carries the risk of SQL injection because you're constructing the SQL on the fly.