MySQL GROUP BY and JOIN - mysql

Guys what's wrong with this SQL query:
$sql = "SELECT
res.Age,
res.Gender,
answer.*,
$get_sum,
SUM(CASE WHEN res.Gender='Male' THEN 1 else 0 END) AS males,
SUM(CASE WHEN res.Gender='Female' THEN 1 else 0 END) AS females
FROM Respondents AS res
INNER JOIN Answers as answer
ON answer.RespondentID=res.RespondentID
INNER JOIN Questions as question
ON answer.Answer=question.id
WHERE answer.Question='Q1'
GROUP BY res.Age
ORDER BY res.Age ASC";
the $get_sum is an array of sql statement derived from another table:
$sum[]= "SUM(CASE WHEN answer.Answer=".$db->f("id")." THEN 1 else 0 END) AS item".$db->f("id");
$get_sum = implode(', ', $sum);
the query above return these values:
Age: 20
item1 0
item2 1
item3 1
item4 1
item5 0
item6 0
Subtotal for Age 20 3
Age: 24
item1 2
item2 2
item3 2
item4 2
item5 1
item6 0
Subtotal for Age 24 9
It should return:
Subtotal for Age 20 1
Subtotal for Age 24 2
In my sample data there are 3 respondents 2 are 24 yrs of age and the other one is 20 years old.
I want to total the number of respondents per age.

$sql = "SELECT
res.Age,
COUNT(1) AS SubTotalRespondentsByAge
$get_sum,
SUM(CASE WHEN res.Gender='Male' THEN 1 else 0 END) AS males,
SUM(CASE WHEN res.Gender='Female' THEN 1 else 0 END) AS females
FROM Respondents AS res
INNER JOIN Answers as answer
ON answer.RespondentID=res.RespondentID
INNER JOIN Questions as question
ON answer.Answer=question.id
WHERE answer.Question='Q1'
GROUP BY res.Age
ORDER BY res.Age ASC"
You cannot include any columns in the select clause that have a many-to-one relationship with age. I've thus removed the res.gender and answers.* columns. What you want is count(1) of the groups (since you group by res.Age).

Related

How to sub count of group by

I have 3 table:
user 1 - n (user_id)feedback(type_id) n - 1 word
User:
id name
1 name1
2 name2
3 name3
4 name4
5 name5
6 name6
Feedback:
id user_id type_id title
1 1 1 title1
2 1 1 title2
3 1 2 title3
4 2 1 title4
5 2 2 title5
6 2 2 title6
7 2 1 title7
Word:
id name category
1 great work FEEDBACK_TYPE
2 good work FEEDBACK_TYPE
I tried to count feedback of each user with below query
Result:
user_id countAllFeedback
1 3
2 4
Query
select
feed.user_id,
count(feed.id) as countAllFeedback
from
feedback feed
group by
feed.user_id;
I want to count feedback of each user by word name
user_id countAllFeedback countGreatWorkFeedback countGoodWorkFeedback
1 3 2 1
2 4 2 2
Please help me with your Query ^^ Thank you
You can try this below script. As you have nothing to collect dynamically from other table, No joining is required. You can use Type_ID as static logic inside CASE statement as those are known list in Word table.
SELECT A.user_id,
COUNT(*) countAllFeedback,
SUM(CASE WHEN A.type_id = 1 THEN 1 ELSE 0 END) countGreatWorkFeedback,
SUM(CASE WHEN A.type_id = 2 THEN 1 ELSE 0 END) countGoodWorkFeedback
FROM Feedback A
GROUP BY A.user_id
If there are possibilities of change in ID, you can do the below-
SELECT A.user_id,
COUNT(A.*) countAllFeedback,
SUM(CASE WHEN B.name = 'great work' THEN 1 ELSE 0 END) countGreatWorkFeedback,
SUM(CASE WHEN B.name = 'good work' THEN 1 ELSE 0 END) countGoodWorkFeedback
FROM Feedback A
INNER JOIN Word B ON A.type_id = B.id
GROUP BY A.user_id
there is no relevance of User table (as per demo data)
This can achieve using this following Query....
SELECT b.user_id,c.name,
SUM(b.user_id) AS 'countAllFeedback',
SUM(CASE WHEN c.name='great work' THEN 1 ELSE 0 END) AS 'countGreatWorkFeedback',
SUM(CASE WHEN c.name='good work' THEN 1 ELSE 0 END) AS 'countGoodWorkFeedback'
FROM tbl_Feedback b
LEFT OUTER JOIN
tbl_Word c
ON b.type_id=c.id
GROUP BY b.user_id,c.name
Note:- Here i'm using Case statement with sum function

Get query result of multiple row in single row?

I have one table with product details in each row where each item is unique.Second table is tax basis which have multiple tax for any single item. I want result which should have single row for each item and all tax related to that item should be in that row with other details
item
item_id name type
1. bike vehicle
tax
id tax_name tax_rate item_id
1. tax1 12 1
2. tax2 13 1
3. tax3 14 1
Result should be:
id name type tax_name_1 tax_rate_1 tax_name_2 tax_rate_2 tax_name_3 tax_rate_3
1 bike vehicle tax1 12 tax2 13 tax3 14
This is a typical table pivot issue, try this:
select
item.*,
max(case when tax.id = 1 then tax_name end) as tax_name_1,
max(case when tax.id = 1 then tax_rate end) as tax_rate_1,
max(case when tax.id = 2 then tax_name end) as tax_name_2,
max(case when tax.id = 2 then tax_rate end) as tax_rate_2,
max(case when tax.id = 3 then tax_name end) as tax_name_3,
max(case when tax.id = 3 then tax_rate end) as tax_rate_3
from item
join tax
group by item.id
I think simple inner join will do this task for you
SELECT item.*, tax.taxname,tax.taxrate
FROM table1
INNER JOIN tax
ON item.id=tax.id;

MySQL select using row counts and grouping

I have 2 tables, orders and order_lineitems.
orders contains the order status info (sold date, invoice no, type of sale, etc)
order_lineitems contains the item(s) for each order in a one to many relationship.
Since we provide shipping info by line item, ship_date is in the order_lineitems table, null if not shipped, a date if it is shipped.
I am trying to pull the orders where all items have shipped by comparing the number of line item rows against the line item rows that have a ship date. While I have successfully pulled all that info, I am unable to make the last step, limiting the result set to include only the completely shipped orders (number of rows = number of rows where ship_date is not null).
I know I am missing something simple, but just don't see it..
select sum(custom.lineitems) as totalitems, sum(custom.shipped) as totalshipped,
custom.invoice, z.shipregion
from (
select a.invoice, count(a.invoice) as lineitems, 0 as shipped
from order_lineitem a
group by a.invoice
UNION ALL
select b.invoice, 0 as notshipped, count(b.ship_date) as shipped
from order_lineitem b
where b.ship_date is not null
group by b.invoice
) as custom
left join orders z on custom.invoice = z.invoice
where z.result = 0
and z.respmsg like 'Approved%'
and z.shipregion <> 'PENDING'
and z.cancelorder = 0
group by custom.invoice;
This returns a result set like so (one row for each invoice in the DB)
totalitems totalshipped invoice shipregion
4 2 1000 REGION08
1 1 10001 REGION07
1 1 10004 REGION05
3 1 10006 REGION05
2 2 10007 REGION04
1 1 10008 REGION08
7 7 10009 REGION01
1 1 1001 REGION08
What I am looking for is a result set like this - only where totalitems = totalshipped
totalitems totalshipped invoice shipregion
1 1 10001 REGION07
1 1 10004 REGION05
2 2 10007 REGION04
1 1 10008 REGION08
7 7 10009 REGION01
1 1 1001 REGION08
Use HAVING clause
SELECT a.invoice, z.shipregion, COUNT(a.invoice) AS lineitems,
SUM(CASE WHEN a.ship_date IS NOT NULL THEN 1 ELSE 0 END) AS shipped
FROM order_lineitem a
LEFT JOIN orders z ON a.invoice = z.invoice AND z.result = 0 AND z.cancelorder = 0 AND
z.respmsg LIKE 'Approved%' AND z.shipregion <> 'PENDING'
GROUP BY a.invoice HAVING lineitems = shipped
OR
SELECT a.invoice, a.shipregion, a.lineitems, a.shipped
FROM (SELECT a.invoice, z.shipregion, COUNT(a.invoice) AS lineitems,
SUM(CASE WHEN a.ship_date IS NOT NULL THEN 1 ELSE 0 END) AS shipped
FROM order_lineitem a
LEFT JOIN orders z ON a.invoice = z.invoice AND z.result = 0 AND z.cancelorder = 0 AND
z.respmsg LIKE 'Approved%' AND z.shipregion <> 'PENDING'
GROUP BY a.invoice
) AS a WHERE a.lineitems = a.shipped
One more outer query needed.
select * from
(
\\Your whole query here
) as Result
where Result.totalitems = Result.totalshipped

SQL select ordered product quantities based on delivery date sorted by week

I am not sure if my question is clear but what I need is quite complex query. I am unable to put it together myself.
We have tables orders, order_items, products, deliveries and delivery_items. Orders is the main order table. Order_items table holds the list of ordered products in certain order. Products is the main products table and deliveries/delivery_items tables hold the list of delivered order_items (we can deliver entire order or only partially).
This is the 'stripped down' table structure:
ORDERS:
ID ORDER_NUMBER DELIVERY_DATE STATUS
-------------------------------------------------
1 2013-00001 Unixtimestamp Closed
2 2013-00002 Unixtimestamp Open
...
PRODUCTS:
ID CODE
-----------------------
1 Product 1
2 Product 2
3 Product 3
...
ORDER ITEMS:
ID ORDER_ID PRODUCT_ID QTY
-----------------------------------------
1 1 1 2
2 1 2 5
3 1 3 1
4 2 3 10
DELIVERIES:
ID ORDER_ID DELIVERY_NUMBER TYPE
---------------------------------------------
1 1 2013-00001 Full
2 2 2013-00002 Partial
...
DELIVERY_ITEMS:
ID DELIVERY_ID ORDER_ITEM_ID QTY
------------------------------------------
1 1 1 2
2 1 2 5
3 1 3 1
4 2 4 5
...
Our production demands view where all non-delivered order_items(products) are listed by quantities in the upcoming week schedule. What I need is something like this (quantities here are random):
Product Overdue W0 W1 W2 W3 W4 W5 Later Total
-------------------------------------------------------------------------
Product 1 1 2 0 0 0 0 0 0 3
Product 2 0 3 5 1 0 0 0 4 13
Product 3 2 4 0 7 5 0 0 0 18
...
The entire view is based on current time and order's delivery_date field. The query would need to get ordered product quantities from all open orders, check if some of there products were maybe already delivered and subtract delivered quantities, and on the end sort the result quantities as shown above.
UPDATE: here is SQL Fiddle with above structure with some data http://sqlfiddle.com/#!2/88891/4
---Using Fiddle provided (http://sqlfiddle.com/#!2/88891/34/0)
SELECT P.Name,
sum(case when DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())<=0 then coalesce(OI.Quantity,0)-coalesce(DI.Quantity,0) else 0 END) AS Overdue,
sum(case when DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())<=7 and DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())>0 then coalesce(OI.Quantity,0)-coalesce(DI.Quantity,0) else 0 END) as W0,
sum(case when DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())<=14 and DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())>7 then coalesce(OI.Quantity,0)-coalesce(DI.Quantity,0) else 0 END) as W1,
sum(case when DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())<=21 and DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())>14 then coalesce(OI.Quantity,0)-coalesce(DI.Quantity,0) else 0 END) as W2,
sum(case when DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())<=28 and DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())>21 then coalesce(OI.Quantity,0)-coalesce(DI.Quantity,0) else 0 END) as W3,
sum(case when DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())<=35 and DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())>28 then coalesce(OI.Quantity,0)-coalesce(DI.Quantity,0) else 0 END) as W4,
sum(case when DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())<=42 and DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())>35 then coalesce(OI.Quantity,0)-coalesce(DI.Quantity,0) else 0 END) as W5,
sum(case when DATEDIFF(FROM_UNIXTIME(Delivery_date),curDate())>42 then coalesce(OI.Quantity,0)-coalesce(DI.Quantity,0) else 0 END) as Later,
sum(coalesce(OI.Quantity,0)-coalesce(DI.Quantity,0)) as Total
FROM ORDERS O
INNER JOIN ORDER_ITEMS OI
ON OI.Order_ID = O.ID
INNER JOIN PRODUCTS P on
P.ID = OI.Product_ID
LEFT JOIN DELIVERIES D
ON D.Order_ID = O.ID
LEFT JOIN DELIVERY_ITEMS DI
ON DI.Delivery_ID = D.ID
AND OI.ID = DI.Order_Item_ID
WHERE coalesce(DI.Quantity,0) < OI.Quantity
GROUP BY P.Name
Thanks for the fiddle. This takes care of all the remaining syntax errors. and formats to include 0 in the results.
Coalesce, takes first non-null value in an unlimited series.
FROM_UNIXTIME converts into to valid date time for comparison using datediff.
else statments handle situations when no data matches critiera thus 0 is
evaluated.
where clause eliminates orders with all items delivered or more than all items ordered delivered. (Thus items closed will still be included since they are not fully shipped!)
To get you started, here are a couple of joins:
SELECT o.ORDER_NUMBER, p.CODE, oi.QTY
FROM orders o
JOIN order_items oi ON oi.ORDER_ID = o.ID
JOIN products p ON p.ID = oi.PRODUCT_ID
And:
SELECT d.DELIVERY_NUMBER, d.ORDER_ID, di.ORDER_ITEM_ID, di.QTY
FROM deliveries d
JOIN delivery_items di ON di.DELIVERY_ID = d.ID
What would you have to do to tie those together?

MySQL query to count non-null values in a single row

I'm trying to put together a MYSQL query that will count the number of Non-Null (or better yet, non-zero) values in select fields in a single row and then sort from lowest to highest (based on the count). For example, I have a table with 5 fields... ID, Name, Score_1, Score_2, Score_3. I want to count how many times the value "0" exists in Score_1, Score_2 and Score_3 for each record, then sort from most non zero values to least.
ID Name Score_1 Score_2 Score_3
1 Dan 8 7 0
2 Joe 0 0 3
3 Chris 0 0 0
4 Mike 4 5 5
I assume the query has to look something like this...
Select ID, Name, Score_1, Score_2, Score_3 where (???) ORDER BY (???)
Output should look like this (ID 4 is displayed first since it has the least amount of non-zero entries)...
ID Name Score_1 Score_2 Score_3
4 Mike 4 5 5
1 Dan 8 7 0
2 Joe 0 0 3
3 Chris 0 0 0
I'm somewhat new to mysql query's, so any help would be greatly appreciated. I thought the COUNT function would help, but that function appears to count columns from all rows. Perhaps there is a way to use the COUNT function and limit it to a singel row so it can be sorted by that row count?
This should do what you want:
SELECT ID, Name, Score_1, Score_2, Score_3
FROM Table1
ORDER BY (Score_1 = 0) + (Score_2 = 0) + (Score_3 = 0)
Result:
ID Name Score_1 Score_2 Score_3
4 Mike 4 5 5
1 Dan 8 7 0
2 Joe 0 0 3
3 Chris 0 0 0
try This:
Select id, Count1, Count2, Count3, Count4
From
(Select
Sum(Case When IsNull(Score_1,0) = 0 Then 1 Else 0 End) Count1,
Sum(Case When IsNull(Score_2,0) = 0 Then 1 Else 0 End) Count2,
Sum(Case When IsNull(Score_3,0) = 0 Then 1 Else 0 End) Count3,
Sum(Case When IsNull(Score_4,0) = 0 Then 1 Else 0 End) Count4
From Table
Group By Id) Z -- This column (Id) better not be the PK for this table!!!
Order By Count1 + Count2 + Count3 + Count4

Categories