MySQL, Gracefully Abort if subquery is Null? - mysql

I wrote a query that is throwing an error when it can't find a record:
Error
Column 'product_id' cannot be null
MySQL
INSERT INTO orders (date, product_id, quantity)
VALUES ('11/29/2012', (
SELECT product_id FROM products WHERE product_name = 'Oranges'
), 12)
;
I'm actually iterating in my PHP and some of the product_name records are not going to exist.
Can I say somehow that if the subquery returns nothing, gracefully stop/abort so the PHP can keep iterating?

Try this:
INSERT INTO orders (date, product_id, quantity)
SELECT '11/29/2012', product_id, 12
FROM products
WHERE product_name = 'Oranges'
If there is no matching product, the query will succeed but return no rows modified. If you wish you can read the number of rows modified from PHP when you execute the query.
Notice that if there are multiple products with product_name = 'Oranges' you'll get multiple rows inserted into your table.

If you want a record to be inserted anyway, you can try to fallback to e.g. 0 if no such product exists:
INSERT INTO orders (date, product_id, quantity)
VALUES ('11/29/2012', COALESCE((
SELECT product_id FROM products WHERE product_name = 'Oranges'
), 0), 12);

Related

Insert multiple rows in a table using a subquery

I'm trying to get all the items of the cart_items and add it to the ticket_items.
I've the error "SUBQUERY RETURN MULTIPLE ROWS", but that's what I want, insert all that rows in the ticket_items table in a same query.
INSERT INTO ticket_items (ticket_id, ISBN, quantity)
VALUES (
(SELECT COUNT(ticket_id) FROM ticket),
(SELECT ISBN FROM cart_items WHERE customer_id = 1),
(SELECT quantity FROM cart_items WHERE customer_id = 1)
);
The issue is that you are using an aggregate function (COUNT) that will build exactly one value, but both isbn and quantity occur twice or more. So, the insert that will be built by your query is something like
INSERT INTO ticket_items (ticket_id, ISBN, quantity)
VALUES (10, 1,2,3, 4,5,6);
This is of course not allowed. Since you do a COUNT, you need to apply GROUP BY to create multiple rows which will be inserted.
So your insert command will be something like this:
INSERT INTO ticket_items (ticket_id, ISBN, quantity)
((SELECT COUNT(ticket_id), ISBN, quantity
FROM ticket, cart_items
WHERE customer_id = 1
GROUP BY ISBN, quantity));
This will work correctly and do what you asked for, I created an example here: db<>fiddle
You should check if this is really what you want because for me, it doesn't make sense to insert the count of ticket_id like you describe. But this is something you must check on your own.
Don't use VALUES keyword.
Please see syntax below:
INSERT INTO table2 (column1, column2, column3, ...)
SELECT column1, column2, column3, ...
FROM table1
WHERE condition;
I would first make sure that select gives you the expected result, then issue the insert statement.
Upon review, I believe the issue is with SELECT part.
You should rewrite it as one SELECT:
INSERT INTO ticket_items (ticket_id, ISBN, quantity)
(SELECT
(SELECT COUNT(ticket_id) from ticket) as ticket_id, ISBN, quantity
FROM cart_items
WHERE customer_id = 1)
Good luck!

Subtract product values based on two consecutive dates and product, return a new column called product difference values in MySQL

I have a below table.
Now I need output like below.
Now I need to display the product differences between the consecutive dates based on product group by. For example: in an Apple loop, the first row should return the default value of 3 and then the next row should return the difference value. Similarly, the code should work on for the next product Samsung loop. Then, the result should be passed into a new column as displayed in the required output.
I am totally new to MySQL, however, I have tried my best to use a join to get the desired output, but I have no idea from where to start. Please guide me on this new concept.
Below are my code which I have tried.
CREATE TABLE products (
id numeric,
product text,
date_value DATE,
prod_value numeric);
INSERT INTO products(id, product, date_value, prod_value)
VALUES(1, 'Apple', '2018-01-22', 3);
INSERT INTO products(id, product, date_value, prod_value)
VALUES(2, 'Apple', '2018-01-23', 6);
INSERT INTO products(id, product, date_value, prod_value)
VALUES(3, 'Apple', '2018-01-24', 7);
INSERT INTO products(id, product, date_value, prod_value)
VALUES(4, 'Samsung', '2018-01-22', 1);
INSERT INTO products(id, product, date_value, prod_value)
VALUES(5, 'Samsung', '2018-01-23', 5);
SELECT
current.prod_value,(current.prod_value - previous.prod_value) diff
FROM
products previous
JOIN
products current
ON
current.id = previous.id+1;
You can use correlated sub-query (for older version), if you are runing with newer version of MySQL then you can use lag() :
select p.*,
p.prodvalue - (select coalesce(p2.prodvalue, 0)
from product p1
where p1.product = p.product and p1.id < p.id
order by p1.id desc
limit 1
) as proddiff
from product p;
Use lag():
select p.*,
(p.prod_value -
lag(p.prod_value, 1, 0) over (partition by product order by date_value)
) as prod_diff
from products p;

MySQL ORDER BY two clauses (descending and ascending)

I have this table:
create table products(name text, quantity int);
insert into products values ("A", 10);
insert into products values ("B", 0);
insert into products values ("C", 15);
insert into products values ("D", 0);
insert into products values ("E", 17);
I would like to order by product name, but keep products with quantity = 0 at the bottom, like this:
name quantity
A 10
C 15
E 17
B 0
D 0
I've tried:
select * from products order by quantity desc, name asc; but it but while it correctly keeps quantity = 0 at the bottom, name ordering is reversed. Is ORDER BY even the right thing to use in this case?
SQL Fiddle: http://sqlfiddle.com/#!9/6985a4/1/0
I would use:
order by (quantity = 0), name
Boolean values are ordered with "false" first then "true".
I would use this to be explicit, rather than using a special value such as 'z' -- which won't even work if you have names that start with 'z'.
Try this:
select * from products
order by case when quantity = 0 then "z" else name end asc;
You can try it like this:
SELECT * FROM (
select name, quantity from products WHERE quantity>0 order by name) t1
UNION
select name, quantity from products t2 WHERE quantity<=0;

Counting results from different tables in one query

I have orders, order_attachments, order_preship_check, order_preship_check_attachment, order_log, order_log_attachments tables
The only common thing in these columns are order_id. Right now in the query I am only checking those order_attachments which are type 1 and type 2 using the following query
SELECT
COUNT(file_id) AS totalFiles
FROM
orders_files
WHERE
order_id = '88125'
AND attachment_type IN ('1', '2')
So if it returns I show an ATTACHMENTS icon with that order. Now I need to combine preship attachments and log attachments in this as well. There are no additional checks, individual queries for both are as follows
SELECT
COUNT(file_id) AS totalFiles
FROM
orders_preship_check_attachment
WHERE
order_id = '88125'
and
SELECT
COUNT(id) AS totalFiles
FROM
orders_log_attachment
WHERE
order_id = '88125'
I need to combine these 3 queries in one query. So if each table have 1 records, I just need to get the count of 3, even 3 is not important. Means if any of these records have attachments I need to show the icon, TRUE or FALSE. I hope it makes it more clear
Try this
SELECT SUM(totalFiles) AS totalFiles
FROM (
SELECT COUNT(file_id) AS totalFiles
FROM orders_files
WHERE order_id = '88125' AND
attachment_type IN ('1', '2')
UNION ALL
SELECT COUNT(file_id) AS totalFiles
FROM orders_preship_check_attachment
WHERE order_id = '88125'
UNION ALL
SELECT COUNT(id) AS totalFiles
FROM orders_log_attachment
WHERE order_id = '88125'
) t
or
SELECT COUNT(file_id) AS totalFiles
FROM (
SELECT file_id
FROM orders_files
WHERE order_id = '88125' AND
attachment_type IN ('1', '2')
UNION ALL
SELECT file_id
FROM orders_preship_check_attachment
WHERE order_id = '88125'
UNION ALL
SELECT id
FROM orders_log_attachment
WHERE order_id = '88125'
) t
Use UNION or UNION ALL but this must have the same type and number of columns.
Example code:
SELECT 'Customer' AS type, id, name FROM customer
UNION ALL
SELECT 'Supplier', id, name FROM supplier
If your query contains different number of columns try using joins.

Easiest Way Of Copying a Row in MySQL

what's the easiest way of to clone a row with a different ID in MySQL.
For example:
Products
product_id name price
------------------------
1 a 10
2 b 15
It looks like weird, but I need to clone product with id = 1. So the table will look like:
Products
product_id name price
------------------------
1 a 10
2 b 15
3 a 10
You can use subqueries:
INSERT INTO
donation (name,price)
SELECT name,price
FROM donation
WHERE product_id = 1
INSERT INTO Products (name, price) VALUES ((SELECT name FROM Products WHERE product_id = 1), (SELECT price FROM Products WHERE product_id = 2));
INSERT INTO Products (name, price)
VALUES
((SELECT name, price
FROM Products
WHERE product_id = 1));
If you want to clone a row with a single SQL statement and can't or don't want to list all the values but there is a primary key you can use:
INSERT INTO Products
SELECT *
FROM Products
WHERE product_id = 1
UNION ALL
SELECT *
FROM Products
WHERE product_id = 1
ON DUPLICATE KEY UPDATE product_id=(SELECT MAX(product_id)+1 FROM Products);
This tries to insert two copies of the row into the databse and when the first row insert fails due to a duplicate product_id it uses ON DUPLICATE KEY to update the existing row in the table to the next available product_id, which leaves the original product_id available for when the second row is inserted.