Getting SUM and JOIN to cooperate in a specific query - mysql

I'm stuck with a certain query, it seems to be simple but it seems I might have to split it in different ways to get it to accomplish what I want to do. I tried combining, recombining and I could use some guidance.
Here's a sample table, the output and expected output.
Shop_Table
shop_id | item_type | date
1 soaps 2000-01-01
2 food 2000-01-02
3 appliances 2000-01-03
4 electronics 2000-01-10
5 furniture 2000-01-13
6 misc. 2000-01-15
Instance_Table
instanceid| shop_id | firstname | lastname | number_of_items
11 3 jane doe 2
22 2 jane doe 3
33 1 jane doe 5
44 4 jane doe 6
55 6 jane doe 1
66 5 jane doe 2
The Query
SELECT
IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'indexpensives',
Shop_Table.item_type
),
COALESCE(SUM(Person.number_of_items), 0) AS item_sum
FROM Shop_Table
INNER JOIN Instance_Table AS Instance
ON Instance.shop_id = Shop_Table.shop_id
AND Instance.firstname = 'jane'
AND Instance.lastname = 'doe'
AND Shop_Table.date BETWEEN DATE( '2000-01-01' ) AND DATE( '2000-01-03' )
GROUP BY Shop_Table.item_type
Which returns:
item_type | item_sum
indexpensives | 8
appliances | 2
Output
Which is great so far, and is where I'm stuck. What I really want is the following output:
Expected Output
item_type | item_sum
inexpensives | 8
appliances | 2
electronics | 0
furniture | 0
misc. | 0
I think it's pretty straight forward what I want to do. What's making it a little tough is that it has a little bit of everything.
Grouping and combining sum results (soaps and food into one result)
Returning 0 for the rest of the item types when the date is outside the specified range.
Update:
Turned out a little more complicated than I thought, but it's what I wanted. Thank you everyone for helping out.
SELECT
IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'inexpensives',
Shop_Table.item_type
),
COALESCE(SUM(Person.number_of_items), 0) AS item_sum
FROM Shop_Table
LEFT OUTER JOIN Instance_Table AS Instance
ON Instance.shop_id = Shop_Table.shop_id
AND Instance.firstname = 'jane'
AND Instance.lastname = 'doe'
AND Shop_Table.date BETWEEN DATE( '2000-01-01' ) AND DATE( '2000-01-03' )
GROUP BY IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'inexpensives',
Shop_Table.item_type
)

Change your query to this and you should get the totals you're looking for:
SELECT
IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'indexpensives',
Shop_Table.item_type
),
SUM(Person.number_of_items) AS item_sum
FROM Shop_Table
LEFT OUTER JOIN Instance_Table AS Instance
ON Instance.shop_id = Shop_Table.shop_id
AND Instance.firstname = 'jane'
AND Instance.lastname = 'doe'
AND Shop_Table.date BETWEEN DATE( '2000-01-01' ) AND DATE( '2000-01-03' )
GROUP BY Shop_Table.item_type
Explanation
An outer join will select all rows from one table and matching rows from another. So, a LEFT OUTER JOIN will select all rows from the table on the left side of the join (Shop_Table in this case) and matching rows from the table on the right side of the join (Instance_Table in this case).

First of all replace your inner join with a left join and then make sure you coalesce the sum with a 0, like this:
SELECT
IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'indexpensives',
Shop_Table.item_type
),
COALESCE(SUM(Person.number_of_items), 0) AS item_sum
FROM Shop_Table
LEFT JOIN Instance_Table AS Instance
ON Instance.shop_id = Shop_Table.shop_id
AND Instance.firstname = 'jane'
AND Instance.lastname = 'doe'
AND Shop_Table.date BETWEEN DATE( '2000-01-01' ) AND DATE( '2000-01-03' )
GROUP BY Shop_Table.item_type
Edit:
After the requirement changes, this should be the final query:
SELECT
IF
(
Shop_Table.item_type
IN
(
'soaps',
'food'
),
'inexpensives',
Shop_Table.item_type
) as FinalType,
COALESCE(SUM(Person.number_of_items), 0) AS item_sum
FROM Shop_Table
LEFT OUTER JOIN Instance_Table AS Instance
ON Instance.shop_id = Shop_Table.shop_id
AND Instance.firstname = 'jane'
AND Instance.lastname = 'doe'
AND Shop_Table.date BETWEEN DATE( '2000-01-01' ) AND DATE( '2000-01-03' )
GROUP BY FinalType

Related

Getting distinct values from table

Im having and issue where in my table FarmerGroups I have multiple records by BSI_Code and I am getting double results for GallonsIssued due to this inner join. Is there a way to get the unique value of GallonsIssued or a way to just get results by individual BSI_CODE
With Summary as (
Select B_NAME as Branch, LOC as Location
,SUM(payment) as Gallons
,SUM(case when printed = 1 THEN Fee ELSE NULL END) as FeeCollected
,SUM(case when printed = 0 THEN Fee ELSE NULL END) as FeeNotCollected
,SUM(case when printed = 1 THEN Payment ELSE NULL END) as GallonsIssued
,SUM(case when printed = 0 THEN Payment ELSE NULL END) as GallonsNotIssued
From SicbWeeklyDeliveriesFuel F Inner Join FarmerGroups G ON G.BSI_CODE = F.BSI_CODE AND G.CROP_SEASON = F.CROP_SEASON AND F.B_NAME = G.BRANCH
Where F.CROP_SEASON = #cropseason
Group By B_NAME, LOC
)
SELECT Branch
,Location
,Gallons
,GallonsIssued
,GallonsNotIssued
,FeeCollected
,FeeNotCollected
,((GallonsIssued/Gallons) * 100) as pct_GallonsCollected
FROM Summary
Order by Location, Branch
For SicbWeeklyDeliveriesFuel
BSI_CODE
Payment
LOC
CROP_SEASON
Fee
B_NAME
FNAME
66
125
CZ
5
12.5
DOUGLAS
John K
55
147
OW
5
14.7
CALEDONIA
Tim H
66
95
CZ
5
9.5
DOUGLAS
John K
For Farmer Groups
BSI_CODE
Farmer
CROP_SEASON
BRANCH
TEST_GROUP
66
John K
5
DOUGLAS
1A
55
Tim H
5
CALEDONIA
1B
66
John K
5
DOUGLAS
2A
Your selection for the JOIN of G.BSI_CODE = F.BSI_CODE AND G.CROP_SEASON = F.CROP_SEASON AND F.B_NAME = G.BRANCH does not uniquely define the rows.
You will need to also include F..FNAME = G.Farmer otherwise the first row of SicbWeeklyDeliveriesFuel (BSI_CODE = 66, CROP_SEASON = 5 and B_NAME = DOUGLAS) matches both the first and last rows of FarmerGroups. Likewise the third row also matches the same two rows in FarmerGroups.
The reason for the duplication is the field TEST_GROUP in FarmerGroups Table.
But you don't need this field in the Join.
First,a CTE to get the info you need in the join without duplicates.
then your old join to the new CTE.
Try this:
WITH FarmersGroup AS
(
SELECT DISTINCT
BSI_CODE
, CROP_SEASON
, BRANCH
FROM FarmerGroups
)
, Summary AS
(
SELECT
Branch = B_NAME
, Location = LOC
, Gallons = SUM(payment)
, FeeCollected = SUM(case when printed = 1 THEN Fee ELSE NULL END)
, FeeNotCollected = SUM(case when printed = 0 THEN Fee ELSE NULL END)
, GallonsIssued = SUM(case when printed = 1 THEN Payment ELSE NULL END)
, GallonsNotIssued = SUM(case when printed = 0 THEN Payment ELSE NULL END)
FROM SicbWeeklyDeliveriesFuel F
JOIN FarmerGroup G ON G.BSI_CODE = F.BSI_CODE
AND G.CROP_SEASON = F.CROP_SEASON
AND G.BRANCH = F.B_NAME
WHERE F.CROP_SEASON = #cropseason
GROUP BY
B_NAME, LOC
)
SELECT
Branch
, Location
, Gallons
, GallonsIssued
, GallonsNotIssued
, FeeCollected
, FeeNotCollected
, pct_GallonsCollected = ((GallonsIssued/Gallons) * 100)
FROM Summary
ORDER BY
Location
, Branch
You can use Andy's code above and it should do the job or you can just replace the table join in your current query
Change the following
Inner Join FarmerGroups G ON G.BSI_CODE = F.BSI_CODE AND G.CROP_SEASON = F.CROP_SEASON AND F.B_NAME = G.BRANCH
to
Inner join (select SELECT DISTINCT
BSI_CODE
, CROP_SEASON
, BRANCH
FROM FarmerGroups ) G on
ON G.BSI_CODE = F.BSI_CODE AND G.CROP_SEASON = F.CROP_SEASON AND F.B_NAME = G.BRANCH

sum 2 table got loop sql

SELECT brg_laku.id_brg, SUM( brg_laku.dibeli ) AS sold, SUM( stok_brg.stok ) AS stock
FROM brg_laku, stok_brg
WHERE stok_brg.id_brg = brg_laku.id_brg
GROUP BY stok_brg.id_brg, brg_laku.id_brg
This my sold table:
id_bl id_brg dibeli harga_laku tgl jam
10 BRG-000001 2 30000 2018-03-16 10:48:35
11 BRG-000001 1 35000 2018-03-16 10:48:38
12 BRG-000003 5 30000 2018-03-16 10:48:41
13 BRG-000003 4 35000 2018-03-16 10:47:13
This is the view using code above:
This my stok table:
How to make it sum correctly in SQL?
You should join the result sum
SELECT brg_laku.id_brg, t.sum_stock
, SUM( brg_laku.dibeli ) AS sold
FROM brg_laku
INNER JOIN (
SELECT
stok_brg.id_brg ,
SUM( stok_brg.stok ) sum_stok
FROM stok_brg
) t on t. id_brg = brg_laku.id_brg
group by brg_laku, t.sum_stock

MYSQL | Get two different records from single table

Ok. This is kind of complicated.. I have been trying to achieve it and got no where so far.
I have three tables.
leave_approval
leave_applications
ml_leave_type
Here is leave_approval Table
which is then linked to leave_application from leave_application_id
Now here comes the Tricky Question,
i want two records from table.
I want Total leaves taken by employee in year 2014
I want Total Leaves taken by employee in month november of 2014
so means need two columns with two different approaches yearly and monthly.
i have query so far.
SELECT
LA.employee_id,
DATEDIFF(
LA.approved_to,
LA.approved_from
) AS TotalLeavesTaken,
LAPP.`entitlement_id`,
LAPP.`ml_leave_type_id`,
LA.leave_application_id,
LA.approved_from AS LeaveFrom,
LA.approved_to AS LeaveTo
FROM
leave_approval LA
INNER JOIN leave_application LAPP
ON LAPP.application_id = LA.leave_application_id
WHERE YEAR(LA.approval_date) = 2014
AND LA.`employee_id` = 1
and here is the result i am getting for the query..
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
UPDATE
May i could not clearly explain my problem.
What i am trying to achieve is add two extra columns not remove the columns i have. i mean i am using most/All of the select fields i have
Plus i want the Total Leaves in Year 2014 in one column and Total Leaves in the month in different field for that given year.
so here is kind of example i want.>
ml_leave_type_id | Total Leaves Taken in Year (e-g 2014) | Total Leaves taken in Month (e-g November)
1 10 2
2 6 0
3 4 1
Would something like that do?
Total leaves for employee_id = 1, Year 2014
SELECT
LA.employee_id,
SUM(
DATEDIFF(
LA.approved_to,
LA.approved_from
)
) AS TotalLeavesTaken,
LAPP.`entitlement_id`,
LAPP.`ml_leave_type_id`,
LA.leave_application_id,
LA.approved_from AS LeaveFrom,
LA.approved_to AS LeaveTo
FROM
leave_approval LA
INNER JOIN leave_application LAPP
ON LAPP.application_id = LA.leave_application_id
WHERE YEAR(LA.approval_date) = 2014
AND LA.`employee_id` = 1
GROUP BY LA.`employee_id` = 1
Total leaves for employee_id = 1, Year = 2014 AND Month = 11
SELECT
LA.employee_id,
SUM(
DATEDIFF(
LA.approved_to,
LA.approved_from
)
) AS TotalLeavesTaken,
LAPP.`entitlement_id`,
LAPP.`ml_leave_type_id`,
LA.leave_application_id,
LA.approved_from AS LeaveFrom,
LA.approved_to AS LeaveTo
FROM
leave_approval LA
INNER JOIN leave_application LAPP
ON LAPP.application_id = LA.leave_application_id
WHERE YEAR(LA.approval_date) = 2014
AND MONTH(LA.approval_date) = 11
AND LA.`employee_id` = 1
GROUP BY LA.`employee_id` = 1
I do notice in your dataset however that there are some dates that are ovelapping. For example:
employee_id = 1 is on leave 2014/11/01 - 2014/11/06 AND 2014/11/05 - 2014/11/08. What's the deal with the dates 2014/11/05 and 2014/11/06. How will the system handle this scenario
Edit2: updated select statement after requirements clarified. The update consist of reorganizing with using a JOIN instead of UNION ALL
select a.*, b.*
from (
SELECT
LA.employee_id,
DATEDIFF(
LA.approved_to,
LA.approved_from
) AS TotalLeavesTaken,
LAPP.`entitlement_id`,
LAPP.`ml_leave_type_id`,
LA.leave_application_id,
LA.approved_from AS LeaveFrom,
LA.approved_to AS LeaveTo
FROM
leave_approval LA
INNER JOIN leave_application LAPP
ON LAPP.application_id = LA.leave_application_id
WHERE YEAR(LA.approval_date) = 2014
AND LA.`employee_id` = 1
) a
JOIN (
SELECT
LA.employee_id,
DATEDIFF(
LA.approved_to,
LA.approved_from
) AS TotalLeavesTaken,
LAPP.`entitlement_id`,
LAPP.`ml_leave_type_id`,
LA.leave_application_id,
LA.approved_from AS LeaveFrom,
LA.approved_to AS LeaveTo
FROM
leave_approval LA
INNER JOIN leave_application LAPP
ON LAPP.application_id = LA.leave_application_id
WHERE MONTHNAME(LA.approval_date) = 'November'
AND LA.`employee_id` = 1
) b
ON a.employee_id = b.employee_id

Concatenating row values sql server 2008 r2

I have two tables register and att_bottom and I want to display only the students at a certain building who have been tardy based on today's date with the periods separated by a comma.
This is the way the data is displayed when joining both tables:
Student ID | Building | Period | Grade
12345 2 1 11
12345 2 5 11
43210 2 1 12
I want this:
Student ID | <u>Building | Period | Grade
12345 2 1,5 11
43210 2 1 12
This is my query:
select r.STUDENT_ID,
r.BUILDING ,
(select ab.attendancePeriod + ','
from att_bottom ab
where ab.STUDENT_ID = r.student_id
and ab.building = '2'
and ab.attendance_c ='T'
and ab.SCHOOL_YEAR =2014
CONVERT(date,ab.attendance_date,102) = convert(date,getdate(),102)
FOR XML PATH ('') ) AS PERIODS,
r.GRADE
FROM register r
where r.CURRENT_STATUS = 'A'
and r.BUILDING ='2'
I'm getting all the students at building 2 and even if they don't have an attedance_c of T; a NULL value for Periods is being retrieved:
Student ID | Building | Period | Grade
12345 2 1 , 5 11
43210 2 1 , 12
95687 2 NULL 09
78417 2 NULL 10
20357 2 NULL 11
I have tried and ab.attendancePeriod is Not NULL and I still get the same results.
Any thoughts?
The outer query doesn't listen to any filters in the subquery; it will return NULL for any rows that aren't matched by the join conditions. You need to filter differently. Here is one way (this also eliminates the errant trailing comma, and avoids comparing dates by converting them expensively to strings):
;WITH x AS
(
SELECT DISTINCT s = r.Student_ID, r.building,
p = ab.attendancePeriod, r.grade
FROM dbo.Register AS r
INNER JOIN dbo.att_bottom AS ab
ON r.Student_ID = ab.Student_ID
AND r.building = ab.building
WHERE ab.building = '2'
AND ab.attendance_c = 'T'
AND ab.SCHOOL_YEAR = 2014
AND ab.attendance_date >= CONVERT(DATE, GETDATE())
AND ab.attendance_date < DATEADD(DAY, 1, CONVERT(DATE, GETDATE()))
AND r.building = '2'
AND r.CURRENT_STATUS = 'A'
)
SELECT DISTINCT
[Student ID] = x.s,
x.building,
Period = STUFF((SELECT ',' + x2.p FROM x AS x2 WHERE x2.s = x.s
FOR XML PATH(''),
TYPE).value(N'./text()[]',N'nvarchar(max)'),1,1,''),
x.grade
FROM x;
Another way:
SELECT DISTINCT
r.Student_ID,
r.building,
Period = STUFF(b.p.value(N'./text()[1]', N'nvarchar(max)'),1,1,''),
r.grade
FROM dbo.Register AS r
CROSS APPLY
(
SELECT p = ',' + ab.attendancePeriod
FROM dbo.att_bottom AS ab
WHERE ab.building = '2'
AND ab.attendance_c = 'T'
AND ab.SCHOOL_YEAR = 2014
AND ab.attendance_date >= CONVERT(DATE, GETDATE())
AND ab.attendance_date < DATEADD(DAY, 1, CONVERT(DATE, GETDATE()))
AND ab.student_id = r.student_id
AND ab.building = r.building
FOR XML PATH(''),TYPE
) AS b(p)
WHERE b.p IS NOT NULL
AND r.building = '2'
AND r.CURRENT_STATUS = 'A';
Move the AS PERIODS select to be an inner join to r.

Selecting 1 Instance of a Client's family in MySQL

What I need to return from the query is a list of Heads of Households who have visited in the past year and all of their relatives. Everyone is in the Client table which is joined to the other tables when queried.
The problem is that the query returns a "set" of Family members for each unique DateVisited. I'm hoping to get a set of results that looks like this (I omitted some columns for clarity):
Head of household Relative/child
---------------------------- ---------------------
John Smith - Katie Smith
John Smith Joe Smith
Tim Jones Mike Jones
Tim Jones Sally Jones
Kevin Barnett Corey Barnett
Kevin Barnett Cara Barnett
Kevin Barnett Austin Barnett
SELECT f.HeadOfHouseholdID,
CONCAT( c.lastname, ', ', c.firstname ) AS 'HName',
v.Datevisited, f.RelationshipID, c2.ClientID,
CONCAT( c2.lastname, ', ', c2.firstname ) AS 'Relative Name'
FROM client c
INNER JOIN clientfamily f ON c.ClientID = f.HeadOfHouseholdID
JOIN visits v ON c.clientid = v.clientID
JOIN client c2 ON c2.clientid = f.relativeID
WHERE v.datevisited BETWEEN CURDATE( ) - INTERVAL 1 YEAR AND CURDATE( )
![Query Results][1]
A coworker figured it out:
SELECT f.HeadOfHouseholdID 'HOHID',
CONCAT( c.Lastname, ', ', c.FirstName ) AS 'Head of Household',
c.PhoneNumber AS 'HOH Phone',
f.RelativeID,
CONCAT( C2.Lastname, ', ', C2.FirstName ) AS 'Relative',
Relationship.Description AS 'Relation',
C2.PhoneNumber AS 'Relative Phone', C2.DOB,
(
(
DATE_FORMAT( NOW( ) , '%Y' ) - DATE_FORMAT( C2.DOB, '%Y' )
) -
( DATE_FORMAT( NOW( ) , '00-%m-%d' ) < DATE_FORMAT( C2.DOB, '00-%m-%d' ) )
) AS Age
FROM client c
INNER JOIN clientfamily f ON c.clientid = f.headofhouseholdid
JOIN (
SELECT ClientID, MAX( DateVisited ) AS 'DateVisited'
FROM Visits
GROUP BY ClientID
) v ON c.clientid = v.clientid
JOIN client c2 ON c2.clientid = f.relativeid
JOIN Relationship ON f.RelationshipID = Relationship.RelationshipID
WHERE v.datevisited
BETWEEN CURDATE( ) - INTERVAL 1 YEAR
AND CURDATE( )