Need assistance with T-SQL query UNION, JOINS, COUNT - sql

Looks that I am stumped with query to sum up shipments grouped by by union operator. Today I was working to retrieve total shipments (count(Distinct. U.SjipmentId) delivered by agent, driver (U.AgentCode) to particular country (U.CtryCode, U.CtryName). The last thing I would like to do is to sum all the shipments together to get the total amount of shipments.
Would anyone advise how I this can be achieved in easy and simply way?
Below you can find my most current query.
SELECT U.AgentCode, U.CtryCode, U.CtryName, count(distinct U.Id)
FROM (
select Agent.AgentCode, Addr.CtryCode, Ctry.Name, Ship.Id
from Shipment
LEFT JOIN RouteTab (nolock) ON RoutTbl.Cexp= Shipment.ID
LEFT JOIN Agent (NOLOCK) ON Agent.AgentID = RouteTbl.AgentID
LEFT JOIN Addr (NOLOCK) ON Addr.AddrId = Shipment.AddrId
LEFT JOIN Ctry (NOLOCK) ON Ctry.Id = Addr.Id
WHERE RouteTbl.Bur ='GB01' AND Agent.AgentCode IS NOT NULL
Union ALL
select Driver.DriverCode, Addr.CtryCode, Ctry.Name, Shipment.Id
from Shipment
LEFT JOIN RouteTab (nolock) ON RoutTbl.Cexp= Shipment.Id
LEFT JOIN Driver (NOLOCK) ON Driver.DriverId = RouteTbl.DriverId
LEFT JOIN Addr (NOLOCK) ON Addr.AddrId = Shipment.AddrId
LEFT JOIN Ctry (NOLOCK) ON Ctry.Id = Addr.Id
WHERE RouteTbl.Bur ='GB01' AND Driver.DriverCode IS NOT NULL
) as U
GROUP BY U.AgentCode, U.CtryCode, U.CtryName
ORDER BY U.AgentCode, U.CtryCode, U.CtryName

Union statements need to have the exact same column names, in your code below the Union All command, try this:
select Driver.DriverCode as AgentCode, Addr.CtryCode, Ctry.Name, Shipment.Id
Also change the Ctry.Name to Ctry.Name as CtryName in both your select statements.

You have the same code from your UNION. Good way to use WITH clause.
In your select you don't need a UNION - use a left join and COALESCE instead.
;With r_tab AS
(
select RouteTab.AgentID, Addr.CtryCode, Ctry.Name, Ship.Id,RouteTab.DriverId
from Shipment
LEFT JOIN RouteTab (nolock) ON RouteTab.Cexp= Shipment.ID
LEFT JOIN Addr (NOLOCK) ON Addr.AddrId = Shipment.AddrId
LEFT JOIN Ctry (NOLOCK) ON Ctry.Id = Addr.Id
WHERE RouteTab.Bur ='GB01'
)
SELECT COALESCE(Agent.AgentCode,Driver.DriverCode) AgentCode, U.AgentCode, U.CtryCode, U.CtryName,
count(distinct U.Id)
FROM r_tab U
LEFT JOIN Agent (NOLOCK) ON Agent.AgentID = U.AgentID
AND Agent.AgentCode IS NOT NULL
LEFT JOIN Driver (NOLOCK) ON Driver.DriverId = U.DriverId
AND Driver.DriverCode IS NOT NULL
GROUP BY COALESCE(Agent.AgentCode,Driver.DriverCode), U.CtryCode, U.CtryName
ORDER BY U.AgentCode, U.CtryCode, U.CtryName`enter code here`

Related

How to create distinct count from queries with several tables

I am trying to create one single query that will give me a distinct count for both the ActivityID and the CommentID. My query in MS Access looks like this:
SELECT
tbl_Category.Category, Count(tbl_Activity.ActivityID) AS CountOfActivityID,
Count(tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;
I know the answer must somehow include SELECT DISTINCT but am not able to get it to work. Do I need to create multiple subqueries?
This is really painful in MS Access. I think the following does what you want to do:
SELECT ac.Category, ac.num_activities, aco.num_comments
FROM (SELECT ca.category, COUNT(*) as num_activities
FROM (SELECT DISTINCT c.Category, a.ActivityID
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as caa
GROUP BY ca.category
) as ca LEFT JOIN
(SELECT c.Category, COUNT(*) as num_comments
FROM (SELECT DISTINCT c.Category, co.CommentId
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as aco
GROUP BY c.Category
) as aco
ON aco.CommentId = ac.CommentId
Note that your LEFT JOINs are superfluous because the WHERE clause turns them into INNER JOINs. This adjusts the logic for that purpose. The filtering is also very tricky, because it uses both tables, requiring that both subqueries have both JOINs.
You can use DISTINCT:
SELECT
tbl_Category.Category, Count(DISTINCT tbl_Activity.ActivityID) AS CountOfActivityID,
Count(DISTINCT tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;

Subquery for MAX() value from LeftJoin

I have this query in SQL Server, and i would like to know if it can be shorter ?
i've made a subquery (with a group by) with all the column just to get the MAX() value of 3 dates which come from a table left join.
Ithink i can't do without a subquery to get the MAX() of the 3 dates.
SELECT otherCol1
,otherCol2
,MAX(UnsubscribedDate) AS UnsubscribedDate
,MAX(OpenedDate) AS OpenedDate
,MAX(ClickedDate) AS ClickedDate
FROM (
SELECT otherCol1
,otherCol2
,tu.JoinDate AS UnsubscribedDate
,trkOp.Clicktime AS OpenedDate
,trkRes.Clicktime AS ClickedDate
FROM v_members_email_occurrence vmec
LEFT JOIN tracking_unsubscribed tu WITH (NOLOCK) ON tu.ReportId = vmec.SendingId
LEFT JOIN tracking_opened trkOp WITH (NOLOCK) ON trkOp.ReportId = vmec.SendingId
LEFT JOIN tracking_result trkRes WITH (NOLOCK) ON trkRes.ReportId = vmec.SendingId
WHERE vmec.MemberId = #MemberId
) AS Result
GROUP BY otherCol1
,otherCol2
You can aggregate without a subquery:
SELECT otherCol1
,otherCol2
,MAX(tu.JoinDate) AS UnsubscribedDate
,MAX(trkOp.Clicktime) AS OpenedDate
,MAX(trkRes.Clicktime) AS ClickedDate
FROM v_members_email_occurrence vmec
LEFT JOIN tracking_unsubscribed tu WITH (NOLOCK) ON tu.ReportId = vmec.SendingId
LEFT JOIN tracking_opened trkOp WITH (NOLOCK) ON trkOp.ReportId = vmec.SendingId
LEFT JOIN tracking_result trkRes WITH (NOLOCK) ON trkRes.ReportId = vmec.SendingId
WHERE vmec.MemberId = #MemberId
GROUP BY otherCol1
,otherCol2

Access Subquery On mulitple conditions

This SQL query needs to be done in ACCESS.
I am trying to do a subquery on the total sales, but I want to link the sale to the province AND to product. The below query will work with one or the other: (po.product_name = allp.all_products) AND (p.province = allp.all_province); -- but it will no take both.
I will be including every month into this query, once I can figure out the subquery on with two criteria.
Select
p.province as [Province],
po.product_name as [Product],
all_price
FROM
(purchase_order po
INNER JOIN person p
on p.person_id = po.person_id)
left join
(
select
po1.product_name AS [all_products],
sum(pp1.price) AS [all_price],
p1.province AS [all_province]
from (purchase_order po1
INNER JOIN product pp1
on po1.product_name = pp1.product_name)
INNER JOIN person p1
on po1.person_id = p1.person_id
group by po1.product_name, pp1.price, p1.province
)
as allp
on (po.product_name = allp.all_products) AND (p.province = allp.all_province);
Make the first select sql into a table by giving it an alias and join table 1 to table 2. I don't have your table structure or data to test it but I think this will lead you down the right path:
select table1.*, table2.*
from
(Select
p.province as [Province],
po.product_name as [Product]
--removed this ,all_price
FROM
(purchase_order po
INNER JOIN person p
on p.person_id = po.person_id) table1
left join
(
select
po1.product_name AS [all_products],
sum(pp1.price) AS [all_price],
p1.province AS [all_province]
from (purchase_order po1
INNER JOIN product pp1
on po1.product_name = pp1.product_name)
INNER JOIN person p1
on po1.person_id = p1.person_id
group by po1.product_name, pp1.price, p1.province --check your group by, I dont think you want pp1.price here if you want to aggregate
) as table2 --changed from allp
on (table1.product = table2.all_products) AND (table1.province = table2.all_province);

Count function in a case statement

I have the following query that counts members:
`SELECT
distinct count(cst_recno) AS [Member ID],
adr_country AS Country
FROM
mb_membership
JOIN mb_member_type on mbt_key = mbr_mbt_key
JOIN co_customer ON cst_key=mbr_cst_key and mbr_delete_flag =0 and
cst_delete_flag=0
LEFT JOIN co_individual ON cst_key=ind_cst_key and ind_delete_flag=0
LEFT JOIN co_customer_x_customer ON cxc_cst_key_1 = co_customer.cst_key and
(cxc_end_date is null or datediff(dd,getdate(),cxc_end_date) >=0) and
cxc_rlt_code='Chapter Member'
LEFT JOIN co_chapter ON cxc_cst_key_2=chp_cst_key
LEFT JOIN co_customer_x_address ON cst_cxa_key=cxa_key
LEFT JOIN co_address ON adr_key=cxa_adr_key
LEFT JOIN co_country on adr_country=cty_code
LEFT JOIN co_region on rgn_key=cty_rgn_key
LEFT JOIN vw_client_uli_member_type WITH (NOLOCK) ON cst_key=mem_cst_key
WHERE mbr_join_date >= '7/1/2015' and mbt_code not like '%Council%'
AND ind_int_code <> 'staff' and cst_org_name_dn not like '%urban land ins%'
and cst_eml_address_dn not like '%#uli.org'
and adr_country ='singapore'
group by adr_country`
and the query returns:
Member ID Country
145 Singapore
However, I know that it includes a few duplicates because when I take out a count function and don't have a group by clause it returns 140 distinct rows.
When I check why it includes a few dupes it is caused by one of the dates.
Can you advise? I basically want to run the query that will display 140 distinct count of Member ID:
Member ID Country
140 Singapore
Instead of SELECT distinct count(cst_recno)... use SELECT count(distinct cst_recno)...:
SELECT
count(distinct cst_recno) AS [Member ID],
adr_country AS Country
FROM
mb_membership
JOIN mb_member_type on mbt_key = mbr_mbt_key
JOIN co_customer ON cst_key=mbr_cst_key and mbr_delete_flag =0 and
cst_delete_flag=0
LEFT JOIN co_individual ON cst_key=ind_cst_key and ind_delete_flag=0
LEFT JOIN co_customer_x_customer ON cxc_cst_key_1 = co_customer.cst_key and
(cxc_end_date is null or datediff(dd,getdate(),cxc_end_date) >=0) and
cxc_rlt_code='Chapter Member'
LEFT JOIN co_chapter ON cxc_cst_key_2=chp_cst_key
LEFT JOIN co_customer_x_address ON cst_cxa_key=cxa_key
LEFT JOIN co_address ON adr_key=cxa_adr_key
LEFT JOIN co_country on adr_country=cty_code
LEFT JOIN co_region on rgn_key=cty_rgn_key
LEFT JOIN vw_client_uli_member_type WITH (NOLOCK) ON cst_key=mem_cst_key
WHERE mbr_join_date >= '7/1/2015' and mbt_code not like '%Council%'
AND ind_int_code <> 'staff' and cst_org_name_dn not like '%urban land ins%'
and cst_eml_address_dn not like '%#uli.org'
and adr_country ='singapore'
group by adr_country`

Limiting result sets by future date - SQL

The Query below produces a record for each Entry in the SP_ScheduleEvent Table.
SELECT m.MaterialId, m.MaterialTitle, se.EventDateTime, c.ChannelName
FROM GB_Material m
LEFT OUTER JOIN SP_ScheduleEvent se on se.MaterialName = m.MaterialName
INNER JOIN SP_Schedule s on s.ScheduleID = se.ScheduleID
INNER JOIN GB_Channel c on c.ChannelID = s.ChannelID
WHERE LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%'
I want to limit the result set by the nearest future EventDateTime.
So per material name i would like to see one EventDateTime, which should be the nearest future date to the current time.
And lastly, a record may not exist in the SP_ScheduleEvent table for a particular materialname, in which case there should be null returned for the EventDateTime column
SQLFiddle
How would i go about doing this?
First, your LEFT JOIN is immaterial, because the subsequent joins make it an INNER JOIN. Either use LEFT JOIN throughout the FROM statement or switch to INNER JOIN.
I think you can use ROW_NUMBER():
SELECT t.*
FROM (SELECT m.MaterialId, m.MaterialName, m.MaterialTitle, se.EventDateTime,
ROW_NUMBER() over (PARTITION BY m.MaterialId OVER se.EventDateTime DESC) as seqnum
FROM GB_Material m INNER JOIN
SP_ScheduleEvent se
on se.MaterialName = m.MaterialName INNER JOIN
SP_Schedule s
on s.ScheduleID = se.ScheduleID INNER JOIN
GB_Channel c
on c.ChannelID = s.ChannelID
WHERE se.EventDateTime > getdate() AND
(LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%')
) t
WHERE seqnum = 1
ORDER BY se.EventDateTime;
Use the ROW_NUMBER() function:
WITH cte AS (
SELECT m.MaterialId, m.MaterialTitle, se.EventDateTime, c.ChannelName,
ROW_NUMBER() OVER (PARTITION BY m.MaterialId ORDER BY EventDateTime ASC) AS rn
FROM GB_Material m
LEFT OUTER JOIN SP_ScheduleEvent se on se.MaterialName = m.MaterialName
LEFT OUTER JOIN SP_Schedule s on s.ScheduleID = se.ScheduleID
LEFT OUTER JOIN GB_Channel c on c.ChannelID = s.ChannelID
WHERE LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%'
AND se.EventDateTime > GETDATE()
)
SELECT * FROM cte
WHERE rn=1