Building a mysql query - mysql

I have such a base structure (short version)
Table Product:
product_id, name, status
1, Product 1, 1
2, Product 2, 1
3, Product 3, 1
4, Product 4, 1
99, Product 99, 1
Table Box:
box_id, name
1, Box 1
Table Product_To_Box:
box_id, product_id
1, 1
1, 2
1, 3
1, 4
1, 99
Table Url: (id = product_id from the table 'product')
url_id, id, url, language_id
1, 1, wp.pl, 1
2, 1, wp.pl, 2
3, 2, google.pl, 1
Table Language:
language_id, name
1, English
2, Polish
There is no problem when I need to download all the products from the table 'product', which is not present in table 'url' and are assigned a 'box_id'.
I do this query
SELECT p.product_id AS id FROM product p LEFT JOIN product_to_box p2b ON (p.product_id = p2b.product_id) WHERE NOT EXISTS (SELECT u.id FROM url u WHERE p.product_id = u.id) AND p2b.box_id = '1'
Return 3 products:
Product 3
Product 4
Product 99
However, he needs a query that will return the items as above but additionally also the products that have no connection to each language from the table 'language'
Query should return 4 products:
Product 2 - return the product because the table 'url' no entry for language_id = 2
Product 3
Product 4
Product 99
How would add another language to the table 'language', eg. Vietnam (language_id = 3) and Table 'url' would look like this
1, 1, wp.pl, 1
2, 1, wp.pl, 2
3, 2, google.pl, 1
3, 2, google.pl, 2
3, 3, google.com, 1
3, 3, google.com, 2
3, 4, onet.pl, 1
3, 4, onet.pl, 2
3, 99, interia.pl, 1
3, 99, interia.pl, 2
This query should return all products because the table 'url' there is no reference to language_id = 3
Product 1
Product 2
Product 3
Product 4
Product 99

So I think the problem with you query is the sub query "NOT EXISTS (SELECT u.id FROM url u WHERE p.product_id = u.id)". Currently your SQL query doesn't even look at the language table, so it doesn't matter if there is matching language_ids. However, if you want join all the tables together and show all product_to_box.id = 1 then the following query would work:
SELECT p.product_id AS id, url_id, url, l.name FROM product p LEFT JOIN product_to_box p2b ON (p.product_id = p2b.product_id)
LEFT JOIN url ON (p.product_id = url.id) LEFT JOIN language l ON (url.language_id = l.language_id)
WHERE p2b.box_id = '1';
This outputs the following:
+------+--------+-----------+---------+
| id | url_id | url | name |
+------+--------+-----------+---------+
| 1 | 1 | wp.pl | English |
| 1 | 2 | wp.pl | Polish |
| 2 | 3 | google.pl | English |
| 3 | NULL | NULL | NULL |
| 4 | NULL | NULL | NULL |
| 99 | NULL | NULL | NULL |
+------+--------+-----------+---------+
6 rows in set (0.00 sec)
The below SQL will show all product_id from the table 'product', that is not found in the table 'url' and do not have assigned all language_id from table 'language'. It checks to see if url.id or language_id is null.
SELECT p.product_id AS id, url_id, url, l.name FROM product p LEFT JOIN product_to_box p2b ON (p.product_id = p2b.product_id)
LEFT JOIN url ON (p.product_id = url.id) LEFT JOIN language l ON (url.language_id = l.language_id)
WHERE url.id is null or l.language_id is null;
+------+--------+------+------+
| id | url_id | url | name |
+------+--------+------+------+
| 3 | NULL | NULL | NULL |
| 4 | NULL | NULL | NULL |
| 99 | NULL | NULL | NULL |
+------+--------+------+------+
3 rows in set (0.00 sec)

Related

SQL: Query to find multiple items with a common value in one field

I have a DB that is structured like this:
UserInfo
----------
Id (INT PK AI)
UserId (INT)
InfoTypeId (INT FK -> InfoType.Id)
Value (varchar)
InfoType
----------
Id (INT PK AI)
InfoTypeName (VARCHAR)
The rows represent information for a given user. For instance, I may have the following rows in the InfoType table:
Id | InfoTypeName
-----------------
1 | "First Name"
2 | "Last Name"
3 | "Age"
and rows in UserInfo like this:
Id | UserId | InfoTypeId | Value
--------------------------------
1 | 1 | 1 | "John"
2 | 1 | 2 | "Smith"
3 | 1 | 3 | "20"
4 | 2 | 1 | "John"
5 | 2 | 2 | "Doe"
6 | 2 | 3 | "30"
7 | 3 | 1 | "Jane"
8 | 3 | 2 | "Doe"
9 | 3 | 3 | "25"
10 | 4 | 1 | "John"
11 | 4 | 2 | "Smith"
12 | 4 | 3 | "25"
I want to create a query that deletes all rows from the UserInfo table that refer to a user whose first name is "John" and last name is "Smith". This means that I want to delete rows 1, 2, 3, 10, 11, and 12. I can't figure out how to write the query.
EDIT:
I was able to get a list of userIDs with
SELECT DISTINCT ui1.UserId
FROM UserInfo ui1
INNER JOIN UserInfo ui2
ON ui1.UserId = ui2.UserId
WHERE (ui1.InfoTypeId = 1 AND ui1.Value = "John" AND ui2.InfoTypeId = 2 AND ui2.Value = "Smith");
And further, a list of rows to delete with
SELECT * FROM UserInfo ui WHERE ui.UserId IN
(SELECT DISTINCT ui1.UserId
FROM UserInfo ui1
INNER JOIN UserInfo ui2
ON ui1.UserId = ui2.UserId
WHERE (ui1.InfoTypeId = 1 AND ui1.Value = "John" AND ui2.InfoTypeId = 2 AND ui2.Value = "Smith"));
But when I replace the SELECT with a DELETE as such:
DELETE FROM UserInfo WHERE UserId IN
(SELECT DISTINCT ui1.UserId
FROM UserInfo ui1
INNER JOIN UserInfo ui2
ON ui1.UserId = ui2.UserId
WHERE (ui1.InfoTypeId = 1 AND ui1.Value = "John" AND ui2.InfoTypeId = 2 AND ui2.Value = "Smith"));
I get
SQL Error [1093] [HY000]: You can't specify target table 'UserInfo' for update in FROM clause
You can use join here:
delete ui
from UserInfo ui join
(select ui.UserId,
max(case when it.InfoTypeName = 'First name' then value end) as first_name,
max(case when it.InfoTypeName = 'Last name' then value end) as last_name
from UserInfo ui join
InfoType it
on ui.InfoTypeId = it.id
group by ui.UserId
) uu
using (UserId)
where uu.first_name = 'John' and uu.last_name = 'Smith';

Select 2 matching columns on base of 3rd column

I want to match id's of column 1 from column 2.
_________________________________
| uid | profile_id | status |
|------|-------------|-----------
| 1 | 2 | checked |
| 2 | 1 | checked |
| 3 | 4 | unchecked|
| 4 | 1 | unchecked|
| 4 | 3 | checked |
| 1 | 4 | checked |
...
This is my table. I want to show the result of same values that match from id1 to id2 and status is checked. Following is the output:
__________________________
| uid | profile_id | status |
|------|-------------|-----------
| 1 | 2 | checked |
| 2 | 1 | checked |
...
Because id1 1 check the id2 2 and vice versa.
I done the following code.
SELECT
`aa`.`uid` AS `uid`,
`aa`.`profile_id` AS `profile_id`,
`aa`.`match_type` AS `match_type`
FROM
(
`matched_profiles` `aa`
left join `matched_profiles` `ab` on(
(`aa`.`uid` = `ab`.`profile_id`)
)
)
where
(
(`aa`.`uid` = `ab`.`profile_id`)
and (`ab`.`uid` = `aa`.`profile_id`)
);
but above code also show me the unchecked result.
Great question. Because of the structure of the data int this table, you can do this by joining the table to itself. You will have two sets of data, replicas of one another.
You want to make sure there is a two-way match between uid and profile_id.
You also want the status for both directions to be checked
SELECT
a.*
FROM matched_profiles a
inner join matched_profiles a2 on
a.uid = a2.profile_id
and a.profile_id = a2.uid
and a.status = 'checked'
and a2.status = 'checked';
You have a kind of explicit/inexplicit join going on. I reformatted to be completely explicit for clarity. You just need to add a filter to make sure all status values are checked.
Below is the SQL to build your test schema, which is nice to provide when asking these questions for SO users.
create table matched_profiles (
uid int,
profile_id int,
status varchar(18)
);
insert into matched_profiles
values
(1, 2, 'checked'),
(2, 1, 'checked'),
(3, 4, 'unchecked'),
(4, 1, 'unchecked'),
(4, 3, 'checked'),
(1, 4, 'checked');

MySQL: Join Conditions based on a Table on a later Join

SELECT
p.Sku,
p.Barcode,
s.AmountIncl,
CASE
WHEN SUM(dl.Qty) IS NULL THEN 0
ELSE SUM(dl.Qty)
END AS Qty,
CASE
WHEN SUM(dl.SubTotal) IS NULL THEN 0
ELSE SUM(dl.SubTotal)
END AS SubTotal
FROM
Product AS p
LEFT JOIN
DocumentLine AS dl ON p.Sku = dl.Sku
LEFT JOIN
Document AS d ON dl.DocumentId = d.DocumentId
AND d.DocumentTypeEnum = 'Order'
AND d.PaymentStatusEnum = 'Paid'
LEFT JOIN
StandardPrice AS s ON p.ProductId = s.ProductId
WHERE
p.Barcode IS NOT NULL
GROUP BY p.Sku
I'm trying to get Qty and SubTotal figures grouped by Sku for Orders that have actually been paid.
These orders can be identified in the Document table with the conditions (d.DocumentTypeEnum = 'Order' & d.PaymentStatusEnum = 'Paid').
The financial data (Qty & SubTotal) live in DocumentLine.
How do I exclude orders/data in the DocumentLine table that are linked to a Document (linked via DocumentId) where DocumentTypeEnum IS NOT an 'Order', and the PaymentStatusEnum IS NOT 'Paid' ?
At this moment the DocumentLine includes Paid Orders, Failed Orders, Unpaid Orders, Pending Orders, Carts, etc. So the Qty and SubTotal data is way higher than we actually have sold/generated.
NOTE: I still want to show all Sku WHERE Barcode IS NOT NULL. So if there is no date in DocumentLine for a Sku the Qty and SubTotal values should be 0 ?
I'm querying in MySQL
Sample Data
Product
Sku | Barcode
1 | A
2 | B
3 |
4 | C
5 |
6 | D
DocumentLine
Sku | Qty | SubTotal | DocumentId
1 | 1 | 100 | 123
2 | 1 | 150 | 124
4 | 2 | 400 | 125
6 | 1 | 120 | 128
1 | 2 | 200 | 129
4 | 1 | 200 | 131
3 | 1 | 600 | 127
Document
DocumentId | DocumentTypeEnum | PaymentStatusEnum
123 | Order | Paid
124 | Cart | NotApplicable
125 | Order | Pending
126 | Cart | NotApplicable
127 | Cart | NotApplicable
128 | Order | Failed
129 | Order | Paid
130 | Cart | NotApplicable
131 | Order | Paid
Result:
Sku | Barcode | AmountIncl | Qty | SubTotal
1 | A | 50 | 3 | 300
2 | B | 60 | 0 | 0
4 | C | 40 | 1 | 200
6 | D | 80 | 0 | 0
SELECT p.Sku, p.Barcode, s.AmountIncl,
CASE WHEN SUM(doc.Qty) IS NULL THEN 0
ELSE SUM(doc.Qty)
END AS Qty,
CASE WHEN SUM(doc.SubTotal) IS NULL THEN 0
ELSE SUM(doc.SubTotal)
END AS SubTotal
FROM Product AS p
LEFT JOIN (Select dl.sku as sku, dl.qty as qty, dl.subtotal as subtotal
from DocumentLine dl, Document d where dl.DocumentId = d.DocumentId
AND d.DocumentTypeEnum = 'Order' AND d.PaymentStatusEnum = 'Paid') AS doc
ON p.Sku = doc.Sku
LEFT JOIN StandardPrice AS s ON p.ProductId = s.ProductId
WHERE p.Barcode IS NOT NULL
GROUP BY p.Sku
You can try this. just use LEFT JOIN base on Product table and filter Barcode which you want to get. with condition aggregate function to make it.
CREATE TABLE Product(
Sku INT,
Barcode VARCHAR(10)
);
INSERT INTO Product VALUES (1,'A');
INSERT INTO Product VALUES (2,'B');
INSERT INTO Product VALUES (3,'');
INSERT INTO Product VALUES (4,'C');
INSERT INTO Product VALUES (5,'');
INSERT INTO Product VALUES (6,'D');
CREATE TABLE DocumentLine(
Sku INT,
Qty INT,
SubTotal INT,
DocumentId INT
);
INSERT INTO DocumentLine VALUES (1, 1, 100,123);
INSERT INTO DocumentLine VALUES (2, 1, 150,124);
INSERT INTO DocumentLine VALUES (4, 2, 400,125);
INSERT INTO DocumentLine VALUES (6, 1, 120,128);
INSERT INTO DocumentLine VALUES (1, 2, 200,129);
INSERT INTO DocumentLine VALUES (4, 1, 200,131);
INSERT INTO DocumentLine VALUES (3, 1, 600,127);
CREATE TABLE Document(
Sku INT,
DocumentTypeEnum VARCHAR(50),
PaymentStatusEnum VARCHAR(50)
);
INSERT INTO Document VALUES (123 ,'Order', 'Paid');
INSERT INTO Document VALUES (124 ,'Cart', 'NotApplicable');
INSERT INTO Document VALUES (125 ,'Order', 'Pending');
INSERT INTO Document VALUES (126 ,'Cart', 'NotApplicable');
INSERT INTO Document VALUES (127 ,'Cart', 'NotApplicable');
INSERT INTO Document VALUES (128 ,'Order', 'Failed');
INSERT INTO Document VALUES (129 ,'Order', 'Paid');
INSERT INTO Document VALUES (130 ,'Cart', 'NotApplicable');
INSERT INTO Document VALUES (131 ,'Order', 'Paid');
Query 1:
select
p.sku,
Barcode,
SUM(case when DocumentTypeEnum = 'Order' and PaymentStatusEnum = 'Paid' then Qty else 0 end) 'Qty',
SUM(case when DocumentTypeEnum = 'Order' and PaymentStatusEnum = 'Paid' then SubTotal else 0 end) 'SubTotal'
from product p
left join DocumentLine dl on dl.sku = p.sku
left join Document d on dl.DocumentId = d.Sku
WHERE Barcode <> '' OR Barcode IS NOT NULL
group by p.sku,Barcode
Results:
| sku | Barcode | Qty | SubTotal |
|-----|---------|-----|----------|
| 1 | A | 3 | 300 |
| 2 | B | 0 | 0 |
| 4 | C | 1 | 200 |
| 6 | D | 0 | 0 |
If you don't want the document there are not Order and Paid use an INNER JOIN for Document and not left join
SELECT p.Sku
, p.Barcode
, s.AmountIncl
, CASE WHEN SUM(dl.Qty) IS NULL THEN 0
ELSE SUM(dl.Qty)
END AS Qty
, CASE WHEN SUM(dl.SubTotal) IS NULL THEN 0
ELSE SUM(dl.SubTotal)
END AS SubTotal
FROM Product AS p
LEFT JOIN DocumentLine AS dl ON p.Sku = dl.Sku
INNER JOIN Document AS d ON dl.DocumentId = d.DocumentId
AND d.DocumentTypeEnum = 'Order'
AND d.PaymentStatusEnum = 'Paid'
LEFT JOIN StandardPrice AS s ON p.ProductId = s.ProductId
WHERE p.Barcode IS NOT NULL
GROUP BY p.Sku

MySQL get products with multiple filter values

Question:
How would I get all products that are Red and size S?
That would mean that I only get product 2: Product (color:green,red => size:S)
Description:
I'm running into a problem with selecting products from a database that have certain filters applied. Below you will find my tables and see SQLfiddle for real SQL.
Table product:
id | title
------------------------------
1 | Product (color:green => size:S)
2 | Product (color:green,red => size:S)
3 | Product (color:red)
Table filter
id | name
------------
1 | Color
2 | Size
Table filter_value
id | filter_id | name
---------------------
1 | 1 | green
2 | 1 | red
3 | 2 | S
Table product_filter_value
id | product_id | filter_id | filter_value_id
---------------------------------------------
1 | 1 | 1 | 1
2 | 1 | 2 | 3
3 | 2 | 1 | 1
4 | 2 | 1 | 2
5 | 2 | 2 | 3
6 | 3 | 1 | 2
To select all products with the filter value red, I run this query:
# ALL COLOR RED
SELECT p.*
FROM product p
LEFT JOIN product_filter_value pfv ON p.id = pfv.product_id
WHERE (pfv.filter_id IN ('1'))
AND (pfv.filter_value_id IN ('2'))
GROUP BY p.id
LIMIT 10 OFFSET 0;
To select all products with the filter value S, I run this query:
# ALL SIZE S
SELECT p.*
FROM product p
LEFT JOIN product_filter_value pfv ON p.id = pfv.product_id
WHERE (pfv.filter_id IN ('2'))
AND (pfv.filter_value_id IN ('3'))
GROUP BY p.id
LIMIT 10 OFFSET 0;
But how would I get all products that are Red and size S?
That would mean that I only get product 2: Product (color:green,red => size:S)
I can't get the sql fiddle to work, but I thought I'd give this a shot anyway. You might have to have different joins for the different filter types. The join with the table alias pfv1 is for filter1, and pfv2 is for filter 2.
SELECT *
FROM product p
LEFT JOIN product_filter_value pfv1 ON p.id = pfv1.product_id and pfv1.filter_id = 1
LEFT JOIN product_filter_value pfv2 ON p.id = pfv2.product_id and pfv2.filter_id = 2
WHERE (pfv1.filter_id IN ('1'))
AND (pfv1.filter_value_id IN ('2'))
and (pfv2.filter_id IN ('2'))
AND (pfv2.filter_value_id IN ('3'))
GROUP BY p.id
LIMIT 10 OFFSET 0;
It may be interesting to look at the whole set without the where clause:
SELECT *
FROM product p
LEFT JOIN product_filter_value pfv1 ON p.id = pfv1.product_id and pfv1.filter_id = 1
LEFT JOIN product_filter_value pfv2 ON p.id = pfv2.product_id and pfv2.filter_id = 2
+ ------- + ----------------------------------- + ------- + --------------- + -------------- + -------------------- + ------- + --------------- + -------------- + -------------------- +
| id | title | id | product_id | filter_id | filter_value_id | id | product_id | filter_id | filter_value_id |
| 1 | Product (color:green => size:S) | 1 | 1 | 1 | 1 | 2 | 1 | 2 | 3 |
| 2 | Product (color:green,red => size:S) | 3 | 2 | 1 | 1 | 5 | 2 | 2 | 3 |
| 2 | Product (color:green,red => size:S) | 4 | 2 | 1 | 2 | 5 | 2 | 2 | 3 |
| 3 | Product (color:red) | 6 | 3 | 1 | 2 | | | | |
+ ------- + ----------------------------------- + ------- + --------------- + -------------- + -------------------- + ------- + --------------- + -------------- + -------------------- +
4 rows
Product 2 has two records because it has two colors, while product 3 has no values for the last four columns because it has no size attribute.
You could get the 2 elements individually, join them and then filter them (in an easy to read word format)
given (and your sql fiddle doesn't load by the way)
/*
drop table if exists product;
create table product(id int ,title varchar(50));
insert into product values
( 1 , 'Product (color:green => size:S)' ),
( 2 , 'Product (color:green,red => size:S)' ),
( 3 , 'Product (color:red)' );
drop table if exists filter;
create Table filter(id int, name varchar(5));
insert into filter values
( 1 , 'Color'),
( 2 , 'Size');
drop table if exists filter_value;
create table filter_value( id int, filter_id int, name varchar(5));
insert into filter_value values
( 1 , 1 , 'green'),
( 2 , 1 , 'red'),
( 3 , 2 , 'S');
drop table if exists product_filter_value;
create Table product_filter_value(id int, product_id int, filter_id int, filter_value_id int);
insert into product_filter_value values
( 1 , 1 , 1 , 1),
( 2 , 1 , 2 , 3),
( 3 , 2 , 1 , 1),
( 4 , 2 , 1 , 2),
( 5 , 2 , 2 , 3),
( 6 , 3 , 1 , 2);
*/
The subquery s gets the colors and the subquery t gets the sizes
select u.stitle product
from
(
select s.* , t.* from
(
select p.id sid,p.title stitle,fv.name sfvname,f.name sname
from product p
join product_filter_value pfv on pfv.product_id = p.id
join filter_value fv on fv.id = pfv.filter_value_id
join filter f on f.id = fv.filter_id
where f.name = 'color'
) s
join
(
select p.id tid,p.title ttitle,fv.name tfvname,f.name tfname
from product p
join product_filter_value pfv on pfv.product_id = p.id
join filter_value fv on fv.id = pfv.filter_value_id
join filter f on f.id = fv.filter_id
where f.name = 'size'
) t on s.stitle = t.ttitle
) u
where u.sname = 'color' and u.sfvname = 'red' and u.tfname = 'Size' and u.tfvname = 'S'
Lets start with the simple basis of looking for one thing.
It could be a size, color or anything else your real data has.
The query could be
SELECT
pfv.product_id,
f.name
from
product_filter_value pfv
join filter_value fv
ON pfv.filter_value_id = fv.id
AND fv.name = "what you are looking for"
JOIN filter f
ON pfv.filter_id = f.id
Now, you could do multiple for each unique joined in the same query.
I will suffix the alias tables for each unique thing being filtered.
You could even add a brand if so applicable by an additional subquery join such as ProdBrand alias
SELECT
p.product_id,
p.title,
ProdColor.name as Color,
ProdSize.name as Size
from
Product p
JOIN ( select
pfv.Product_id,
fv.name
from
product_filter_value pfv
JOIN filter_value fv
ON pfv.filter_value_id = fv.id
AND fv.name = "red"
JOIN filter f
ON pfv.filter_id = f.id
AND f.name = "Color" ) ProdColor
ON P.id = ProdColor.Product_ID
JOIN ( select
pfv.Product_id,
fv.name
from
product_filter_value pfv
JOIN filter_value fv
ON pfv.filter_value_id = fv.id
AND fv.name = "S"
JOIN filter f
ON pfv.filter_id = f.id
AND f.name = "Size" ) ProdSize
ON P.id = ProdSize.Product_ID
Now, if you wanted to try for multiple colors or sizes,
just change each subquery's criteria to something like...
AND INLIST( fv.name, "red", "blue" ) -- example for red OR blue
AND INLIST( fv.name, "S", "M" ) -- example for Small OR Medium
It may be a long query, but looking at each piece individually is quite simple. Its purpose is the one criteria a color, size, brand, etc. You just are joining on that as part of your overall condition to get products.
I working SQLFiddle from your original schema can be found at this working sample

SQL select - include rows that don't exist?

Can someone help out with this SQL?
SELECT A.post_id, B.post_id, A.ul_value as "likes", B.ul_value as "dislikes"
FROM wp_like_dislike_counters as A,
wp_like_dislike_counters as B
where
A.post_id = B.post_id
and A.ul_key = 'u_like'
and B.ul_key = 'u_dislike'
The results I'm getting:
3, 3, 1, 2
4, 4, 3, 2
The results I want:
3, 3, 1, 2
4, 4, 3, 2
1, 1, 0, 1
Below is the data in the table. Notice that the row with id of 32 has a post_id of 1 along with 1 for the l_value (meaning 1 dislike). Since this post_id has no row for "likes", I would like to display this as a 0 as in the results I want above. Is this possible with this table structure?
id | post_id | l_key | l_value
---+---------+-----------+---------
35 | 3 | u_dislike | 2
34 | 4 | u_dislike | 2
31 | 4 | u_like | 3
32 | 1 | u_dislike | 1
36 | 3 | u_like | 1
You want to do this with conditional aggregation, not a join:
select post_id,
sum(case when ul_key = 'u_like' then ul_value else 0 end) as likes,
sum(case when ul_key = 'u_dislike' then ul_value else 0 end) as dislikes
from wp_like_dislike_counters
group by post_id;