I have 2 tables
orders
order_id
1
2
3
orders_details
order_id
sku
ordered
received
1
abc
10
10
1
xyz
10
10
2
abc
10
6
2
xyz
10
0
3
abc
10
0
4
xyz
10
0
I would like to add to the orders table a column called status which would have the results 'complete', 'partial' and 'open' based on what has been received in the orders_details table
result I am trying to acheive
order_id
status
1
complete
2
partial
3
open
The problem is when a different product from the same order_id has a different received result it creates an additional row with the group by.
result I am getting
order_id
status
1
complete
2
open
2
partial
3
open
https://www.db-fiddle.com/f/bFrYguhmcMJ32iFuUcXfDw/3
SELECT
`orders`.`order_id`,
(CASE
WHEN `received` >= `ordered` THEN 'complete'
WHEN `received` < `ordered` AND `received` != 0 THEN 'partial'
WHEN `received` = 0 THEN 'open'
END) AS `status`
FROM
`orders`
LEFT JOIN `orders_details` ON `orders_details`.`order_id` = `orders`.`order_id`
GROUP BY
`order_id`, `status`
;
I might be missing something, but I think you just want:
SELECT `orders`.`order_id`,
CASE SUM(received)
WHEN 0 THEN 'open'
WHEN SUM(ordered) THEN 'complete'
ELSE 'partial'
END AS `status`
FROM `orders`
LEFT JOIN `orders_details`
ON `orders_details`.`order_id` = `orders`.`order_id`
GROUP BY `order_id`;
Here's your updated DB Fiddle
It is a bit hacky, but you can define the order by giving then names of the status an order the should be choosen, and then take the maximum for this order.
Zcomplete>partial>open
We can thenremove the additional charatcers
Query #1
SELECT
`orders`.`order_id`,
REPLACE (MAX((CASE
WHEN `received` >= `ordered` THEN 'Zcomplete'
WHEN `received` < `ordered` AND `received` != 0 THEN 'partial'
WHEN `received` = 0 THEN 'open'
END)), 'Z', '') AS `status`
FROM
`orders`
LEFT JOIN `orders_details` ON `orders_details`.`order_id` = `orders`.`order_id`
GROUP BY
`order_id`
;
order_id
status
1
complete
2
partial
3
open
View on DB Fiddle
I'm trying to set up an SQL View that returns all details of a contact. Name and Last name are saved in a Table "Person", the contact info is saved in "contact" and the type of contact info (email, phone 1, phone2) is saved in "contact_types".
I want to return all the information in 1 row but I can't really figure it out. So far my best result is with:
SELECT
Person.ID, Person.Title, Person.Firstname, Person.Lastname,
( SELECT MAX(ContactInfo.InfoText) FROM ContactInfo WHERE ContactInfo.ContactTypID = '1' AND ContactInfo.PersonID = Person.ID ) AS Phone_Business,
( SELECT MAX(ContactInfo.InfoText) FROM ContactInfo WHERE ContactInfo.ContactTypID = '2' AND ContactInfo.PersonID = Person.ID ) AS Phone_Private,
( SELECT MAX(ContactInfo.InfoText) FROM ContactInfo WHERE ContactInfo.ContactTypID = '3' AND ContactInfo.PersonID = Person.ID ) AS Phone_Mobile,
( SELECT MAX(ContactInfo.InfoText) FROM ContactInfo WHERE ContactInfo.ContactTypID = '5' AND ContactInfo.PersonID = Person.ID ) AS Email
FROM Person
This statement results in duplicate outputs - 4 identical rows, even with MAX(). It is apparently one row per subquery. How can I only receive 1 row per ID?
I'm quite new to SQL, any suggestions would be helpful!
Edit:
Sample Data:
Table Person:
ID
Title
Firstname
Lastname
1
Mr.
Tom
Selleck
2
Mr.
Fred
Miller
Table ContactInfo
PersonID
InfoText
ContactTypeID
1
tom.selleck#gmail.com
5
2
+1 12345 678
1
1
+1 98765 432
2
Table ContactTypeID
ID
InfoText
1
phone_business
2
phone_private
5
email
Expected Result:
ID
Title
Firstname
Lastname
Phone_Business
Phone_Private
Phone_Mobile
Email
1
Mr.
Tom
Selleck
NULL
+1 98765 432
NULL
tom.selleck#gmail.com
2
Mr.
Fred
Miller
+1 12345 678
NULL
NULL
NULL
It works so far, but I'd get each row 4 times.
You can do it like this:
SELECT P.ID, P.Title, P.Firstname, P.Lastname,
MAX(CASE WHEN C.ContactTypeID = '1' THEN C.InfoText END) AS Phone_Business,
MAX(CASE WHEN C.ContactTypeID = '2' THEN C.InfoText END) AS Phone_Private,
MAX(CASE WHEN C.ContactTypeID = '3' THEN C.InfoText END) AS Phone_Mobile,
MAX(CASE WHEN C.ContactTypeID = '5' THEN C.InfoText END) AS Email
FROM Person P
LEFT JOIN ContactInfo C
ON P.ID=C.PersonID
GROUP BY P.ID, P.Title, P.Firstname, P.Lastname;
Just a single LEFT JOIN between Person table and ContactInfo. The Person table here acts as a reference table. Then use MAX() with CASE expression (also possible with GROUP_CONCAT()) in SELECT.
Here's a demo fiddle
I have two database tables customers which contains data about customers with the scheme like that:
mysql> SELECT * FROM customers;
customer_id created_at partner_id
1 "2019-08-20 09:17:58" cats
2 "2019-09-12 11:46:37" dogs
and customers_facts which keeps the customers facts in a form of fact_name and corresponding fact_value.
mysql> SELECT * FROM customers_facts;
customer_id fact_name fact_value
1 name Milton
1 city Milan
1 birthday "2019-08-20 09:17:58"
1 company Idaho
2 surname Bloom
2 name Orlando
3 name Milton
3 city Milan
3 birthday "2011-10-20 11:17:58"
3 company Chicago
I want to create a query to get all customer_id where name=Milton and city=Milan sorted by birthday and company. So in my example the results would be:
mysql> SELECT customer_id FROM ....
customer_id
1
3
I have a query which gets all the customers_id where name=Milton and city=Milan
SELECT cf.* FROM customers_facts cf
WHERE cf.customer_id IN (
SELECT cf.customer_id FROM customers_facts cf
WHERE (cf.fact_name,cf.fact_value) IN (('name','Milton'),('city','Milan'))
GROUP BY cf.customer_id
HAVING COUNT(*) = 2
)
But I have no idea on how to sort the results by fact_value How to do it ? Is it even possible with such scheme ?
This is a little tricky. You can't filter easily before aggregating. So, do the filtering in the having clause:
SELECT customer_id
FROM customers_facts
GROUP BY customer_id
HAVING SUM( fact_name = 'name' AND fact_value = 'Milton' ) > 0 AND
SUM( fact_name = 'city' AND fact_value = 'Milan' ) > 0
ORDER BY MAX(CASE WHEN fact_name = 'birthday' THEN fact_value END) DESC,
MAX(CASE WHEN fact_name = 'company' THEN fact_value END)
Ues pivoting logic to turn out the birthday for each matching customer group:
SELECT customer_id
FROM customers_facts
WHERE (fact_name, fact_value) IN (('name', 'Milton'), ('city', 'Milan'))
GROUP BY customer_id
HAVING MIN(fact_name) <> MAX(fact_name)
ORDER BY MAX(CASE WHEN fact_name = 'birthday' THEN fact_value END);
Other than the ORDER BY clause, I used a HAVING clause which ensures that both the matching name and city were present in each matching customer group.
Edit:
Here is your desired ORDER BY clause:
ORDER BY
MAX(CASE WHEN fact_name = 'birthday' THEN fact_value END) DESC,
MAX(CASE WHEN fact_name = 'company' THEN fact_value END); -- ASC is default
I'm attempting to count how many completed events each person in my table have done. The problem I'm running into is that people have multiple jobs in my person table, which means there are purposeful multiple rows per person -- which is making my event table double when I try to do counts.
Here's a SQL Fiddle of my code. Easiest to see ID #1 only has two events completed but the table counts four because they also have two jobs.
Here's my sample schema:
CREATE TABLE persontable
(id INT NOT NULL
, name VARCHAR(255) NOT NULL
, employer VARCHAR(255) NOT NULL
, PRIMARY KEY(id,employer)
);
CREATE TABLE eventtable
(id INT NOT NULL
, name VARCHAR(255) NOT NULL
, eventname VARCHAR(255) NOT NULL
, eventdate DATE NOT NULL
, status VARCHAR(255) NOT NULL
, PRIMARY KEY (id,eventname,eventdate));
INSERT INTO persontable (id,name,employer) VALUES
(1,"Joe","Party Inc."),
(1,"Joe","Body Shop"),
(2,"Puddy","Body Shop"),
(3,"Newman","Postal Service"),
(3,"Newman","Computers Inc."),
(4,"Delores","Mulva LLC"),
(5,"Morty","Executive Raincoats"),
(6,"Helen","Body Shop"),
(7,"Frank","Retired"),
(7,"Frank","Mulva LLC"),
(8,"Estelle","Retired"),
(9,"Mandelbaum","Weight Lifters Guild"),
(9,"Mandelbaum","The Wiz"),
(10,"Fred","The Wiz");
INSERT INTO eventtable (id,name,eventname,eventdate,status) VALUES
(1,"Joe","Mayo Party",5/4/94,"Completed"),
(1,"Joe","Coat Shopping",1/2/95,"Completed"),
(4,"Delores","Play",5/9/94,"Completed"),
(4,"Delores","Name Guessing",3/9/98,"Completed"),
(9,"Mandelbaum","Working Out",3/2/97,"Declined"),
(10,"Fred","Store Sale",8/9/96,"Completed");
And my fairly simple query that's adding the additional counts:
SELECT
p.id,
e.id,
COUNT(DISTINCT CASE WHEN e.status="Completed" THEN e.id ELSE NULL END) AS EVENT,
COUNT(CASE WHEN e.status="Completed" THEN e.id ELSE NULL END) AS YTDAllShiftsComp
FROM persontable p
LEFT JOIN eventtable e ON p.id = e.id
GROUP BY p.id;
My desired outcome for the sample is:
id id EVENT YTDAllShiftsComp
1 1 1 2
2 (null) 0 0
3 (null) 0 0
4 4 1 2
5 (null) 0 0
6 (null) 0 0
7 (null) 0 0
8 (null) 0 0
9 9 0 0
10 10 1 1
Thanks for the help!
Thats what happens when you dont normalize your data.Since each person can attend multiple events and each event can host multiple persons you need an intermediate table which holds the primary keys of both tables,this is called many to many relation.So I Joined just on distinct persons id,eliminating the duplicates,but the real solution is to add a new table.
SELECT
x.id,
e.id,
COUNT(DISTINCT CASE WHEN e.status="Completed" THEN e.id ELSE NULL END) AS EVENT,
COUNT(CASE WHEN e.status="Completed" THEN e.id ELSE NULL END) AS YTDAllShiftsComp
FROM (SELECT id FROM persontable GROUP BY id)x
LEFT JOIN eventtable e ON x.id = e.id
GROUP BY x.id;
You can use correlated subqueries:
SELECT
p.id,
(SELECT COUNT(DISTINCT CASE WHEN e.status="Completed" THEN e.id END)
FROM eventtable e
WHERE p.id = e.id) AS EVENT,
(SELECT COUNT(CASE WHEN e.status="Completed" THEN e.id END)
FROM eventtable e
WHERE p.id = e.id) AS YTDAllShiftsComp
FROM persontable p
GROUP BY p.id;
Demo here
As Georgios mentioned, you need subqueries - but if that sometimes-null 2nd ID column is really needed, you'll want to wrap the main statement to NULL it out if the event count is zero.
SELECT id, if(event=0, NULL, event) as idagain, event, ytdallshiftscomp
FROM (SELECT distinct p.id,
(SELECT count(distinct id) FROM eventtable WHERE id=p.id AND status="Completed") AS EVENT,
(SELECT count(*) FROM eventtable WHERE id=p.id AND status="Completed") AS ytdallshiftscomp
FROM persontable p) q
I'm looking to create a sql statement that will update a large set of data.
What I have is a table like
id, transid, amount, narative1, narative 2, total, active
1 1234 23.2 NULL NULL NULL 1
2 1234 120.33 NULL NULL NULL 1
3 1235 98.00 NULL NULL NULL 1
When there are two rows with the same transid I need to total them put the result in the total column of the first one with that transid and put the second amount in naritive2 of the first instance as well as make the second one inactive. It should ignore single rows for a transid.
The result of what I want to do should be:
id, transid, amount, narative1, narative 2, total, active
1 1234 23.2 NULL 120.33 143.53 1
2 1234 120.33 NULL NULL NULL 0
3 1235 98.00 NULL NULL NULL 1
I know a bit of a thong twister but..
Ideally I'd like to do this in just a MySQL statements. So I don't mind having to do multiple sql statements but I want to avoid connecting it to PHP etc. Its a very large set of data.
This will update only those transactions that have exactly 2 rows (not 1 and not 3 or more).
UPDATE mytable mtu
JOIN (
SELECT minid, maxid, mtmin.amount AS minamt, mtmax.amount AS maxamt
FROM (
SELECT MIN(id) AS minid, MAX(id) AS maxid
FROM mytable mti
GROUP BY
transid
HAVING COUNT(*) = 2
) mt
JOIN mytable mtmin
ON mtmin.id = minid
JOIN mytable mtmax
ON mtmax.id = maxid
) mts
ON id IN (minid, maxid)
SET narative2 = CASE id WHEN minid THEN minamt ELSE NULL END,
total = CASE id WHEN minid THEN minamt + maxamt ELSE NULL END,
active = (id = minid)