SQL LEFT OUTER JOIN with Count - sql

I have this query here:
SELECT a.timeSlot, a.dateSlot, COUNT(concat(b.dateSlot, ' - ', b.timeSlot)) AS counter
FROM CP_VIP_Preview_TimeSlots as a
LEFT OUTER JOIN [CP-VIP-Preview] as b
ON a.timeSlot = b.dateSlot
AND a.dateSlot = b.timeSlot
GROUP BY a.timeSlot, a.dateSlot, a.[order]
ORDER BY a.[order]
What I am trying to do is get a count of each, which this query does, but something is messed up, any rows that have 0 appear as 1 and any row that actually has an items show the correct number, my problem if the row count is 0 its displaying 1....why is it doing that?

Your COUNT(concat(b.dateSlot, ' - ', b.timeSlot)) will always return at least one
Perhaps you can try
sum(IIF(b.dateSlot is null,0,1))

you need to use a HAVING for apply a filter after use group by, that way you do not count the records zero
SELECT a.timeSlot, a.dateSlot, COUNT(concat(b.dateSlot, ' - ', b.timeSlot)) AS counter
FROM CP_VIP_Preview_TimeSlots as a
LEFT OUTER JOIN [CP-VIP-Preview] as b
ON a.timeSlot = b.dateSlot
AND a.dateSlot = b.timeSlot
GROUP BY a.timeSlot, a.dateSlot, a.[order]
ORDER BY a.[order]
HAVING COUNT(concat(b.dateSlot, ' - ', b.timeSlot)) > 0

Related

Many Duplicates, caused by a phone number column. Need to cut down duplicates

See query below returning approx 38K rows. When 'phone' join and column are removed, it cuts down to the correct 15.5K rows.
SELECT
tc.customer_no
,fdn.display_name_short 'name'
,tc.cont_amt
,tc.ref_no
,tc.cont_dt
,tc.cont_type
,tca.fyear
,(ISNULL(street1, 'none') + ' ' + ISNULL(city, 'none') + ' ' + ISNULL(state, 'none')
+ ', ' + ISNULL(postal_code, 'none')) 'address'
,ISNULL(tp.phone, 'none')
,ISNULL(te.address, 'none')
FROM T_CONTRIBUTION tc
JOIN FT_CONSTITUENT_DISPLAY_NAME() fdn
ON tc.customer_no = fdn.customer_no
JOIN T_CAMPAIGN tca
ON tc.campaign_no = tca.campaign_no
LEFT JOIN T_ADDRESS ta
ON tc.customer_no = ta.customer_no AND ta.primary_ind = 'y'
LEFT JOIN T_EADDRESS te
ON tc.customer_no = te.customer_no AND te.primary_ind = 'y'
LEFT JOIN T_PHONE tp
ON tc.customer_no = tp.customer_no
WHERE tca.fyear BETWEEN 2018 AND 2022
AND tc.cont_amt > 0
AND te.inactive = 'N'
AND ta.inactive = 'N'
Any advice as to how i can include the phone number column, while eliminating as many duplicates as possible? I don't have to be highly precise with this query, but need to get the row count down as much as possible. The phone table has about 50 different phone types (ex. 1,2,or 22), and the PK is the phone number. The DB has since moved to using only phone type 1 or 2, but i am searching 4 yrs back which is before they switched to only using two different phone types.
Followed suggestions in comments, ended up with:
CTE to create numbered and grouped rows
WITH cte AS (
SELECT customer_no, phone
, row_number() OVER(PARTITION BY customer_no ORDER BY phone) AS rn
FROM T_PHONE
)
Then referenced said cte in the main query's select.
Finally added
WHERE cte.rn = 1
Which selected the first phone number at random, in each group of customer's phones numbers.

SQL: If there are two rows that contain same record, want it to display one

based on my question above, below is the SQL
SELECT ets_tools.tools_id, ets_borrower.fullname, ets_team.team_name, ets_borrow.time_from,
ets_borrow.time_to, ets_borrow.borrow_id FROM ets_tools
INNER JOIN ets_tools_borrow ON ets_tools.tools_id = ets_tools_borrow.tools_id
INNER JOIN ets_borrow ON ets_borrow.borrow_id = ets_tools_borrow.borrow_id
INNER JOIN ets_borrower ON ets_borrower.badgeid = ets_borrow.badgeid
INNER JOIN ets_team ON ets_team.team_id = ets_borrower.team_id
WHERE ets_tools.borrow_id IS NOT NULL AND ets_borrow.status_id = 1 AND ets_borrow.time_to IS NULL
and the result display like this:
From the image above, we can see that the borrow_id with value 1 display two rows. Now, how to display only one borrow_id for value 1 since its duplicate the same things.
Anyone can help?
Assuming you want to retain the record having the smallest tools_id, you could aggregate by the other columns and take the MIN of tools_id:
SELECT
MIN(ets_tools.tools_id) AS tools_id,
ets_borrower.fullname,
ets_team.team_name,
ets_borrow.time_from,
ets_borrow.time_to,
ets_borrow.borrow_id
FROM ets_tools
INNER JOIN ets_tools_borrow ON ets_tools.tools_id = ets_tools_borrow.tools_id
INNER JOIN ets_borrow ON ets_borrow.borrow_id = ets_tools_borrow.borrow_id
INNER JOIN ets_borrower ON ets_borrower.badgeid = ets_borrow.badgeid
INNER JOIN ets_team ON ets_team.team_id = ets_borrower.team_id
WHERE
ets_tools.borrow_id IS NOT NULL AND
ets_borrow.status_id = 1 AND
ets_borrow.time_to IS NULL
GROUP BY
ets_borrower.fullname,
ets_team.team_name,
ets_borrow.time_from,
ets_borrow.time_to,
ets_borrow.borrow_id;
Try this:
Change the SELECT to SELECT TOP 1 WITH TIES
And at the end add ORDER BY ROW_NUMBER() OVER(PARTITION BY ets_borrow.borrow_id ORDER BY ets_tools.tools_id)

SQL left join returns nothing when no matches

I am writing a stored procedure that adds the counts to two fields. I have the following code:
SELECT Distinct DateTime1,SUM(TICKETREQ1)SUMREQ, SUM(TicketPU1)SUMPU1, (count(*))AS GRADCOUNT
FROM TABLEA
WHERE YEAR = '2015'
AND TicketReq1 > 0
group by DateTime1
Select DISTINCT(DateTime2),SUM(TicketReq2) SUMREQ,SUM(TicketPU2)SUMPU2, (count(*))AS GRADCOUNT
from TABLEA
where TicketReq2 > 0
and YEAR = '2015'
Group by DateTime2;
SELECT Distinct c.DateTime1,SUM(c.TICKETREQ1 + b.TicketReq2)SUMREQ, SUM(c.TicketPU1 + b.TicketPU2)SUMPU1, (count(b.id) + count(c.id))AS GRADCOUNT
FROM TABLEA c
LEFT JOIN TABLEA b
ON (b.DateTime2 = c.DateTime1
AND b.TicketReq2 > 0
AND b.YEAR = '2015')
WHERE c.YEAR = '2015'
AND c.TicketReq1 > 0
group by c.DateTime1
This returns:
For some ceremonies the second query does bring in results and adds them correctly. But if there are no records then it fails.
How can I get it to join the two counts together (Query 1 and 2) so that Query 3 displays both counts even when there is no match
The problem is the SUM statements on query #3. b.TicketReq2 is null, therefore SUM(c.TICKETREQ1 + b.TicketReq2) should encounter an error. Try using ISNULL(b.TicketReq2, 0) in your SUM function calls.
Try full outer join instead of left join
SELECT Distinct c.CeremonyDateTime1,SUM(c.TICKETREQ1 + b.TicketReq2)SUMREQ, SUM(c.TicketPU1 + b.TicketPU2)SUMPU1, (count(b.gid) + count(c.gid))AS GRADCOUNT
FROM ComTicket c
FULL OUTER JOIN ComTicket b
ON (b.CeremonyDateTime2 = c.CeremonyDateTime1
AND b.TicketReq2 > 0
AND b.Gradterm = '201540')
WHERE c.gradterm = '201540'
AND c.TicketReq1 > 0
group by c.CeremonyDateTime1
It might helpful to you..

Return First 4 Rows, then Repeat for Grouping

I am trying to return data that will ultimately populate a label.
Each label is going onto a box, and the box can only have 4 items in it.
If a delivery has more than 4 items, then I need one label per 4.
Each row of data returned will populate one label, so if the delivery contains 9 items, then I need 3 rows of data returned.
Below is my current query, which is returning all items into a comma separated value using Stuff.
I want it so the first 4 rows for the delivery return in the first row, then the next 4 in the second and so on.
My Field LineOrd returns correctly if there are more than 4 lines on the dispatch.
select Distinct
delivery_header.dh_datetime,
delivery_header.dh_number,
order_header.oh_order_number as 'Order No',
order_header_detail.ohd_delivery_name,
order_header_detail.ohd_delivery_address1,
order_header_detail.ohd_delivery_address2,
order_header_detail.ohd_delivery_address3,
order_header_detail.ohd_delivery_town,
order_header_detail.ohd_delivery_county,
order_header_detail.ohd_delivery_postcode,
order_header_detail.ohd_delivery_country,
STUFF((Select ', '+convert(varchar(50),convert(decimal(8,0),DL.dli_qty))+'x '+OLI.oli_description
from delivery_header DH join delivery_line_item DL on DL.dli_dh_id = DH.dh_id join order_line_item OLI on OLI.oli_id = DL.dli_oli_id
Outer APPLY
(select
case when DelCurLine.CurLine <= 4
then '1'
Else
Case when DelCurLine.CurLine <= 8
then '2'
Else '3'
End
End +'-'+order_header.oh_order_number as LineOrd) as StuffLineOrder
Where DH.dh_id = delivery_header.dh_id And StuffLineOrder.LineOrd = LineOrder.LineOrd
FOR XML PATH('')),1,1,'') as Items,
LineOrder.LineOrd
from delivery_header
join delivery_line_item on delivery_line_item.dli_dh_id = delivery_header.dh_id
join order_line_item on order_line_item.oli_id = delivery_line_item.dli_oli_id
join order_header on order_header.oh_id = order_line_item.oli_oh_id
join order_header_detail on order_header_detail.ohd_oh_id = order_header.oh_id
join variant_detail on variant_detail.vad_id = order_line_item.oli_vad_id
join stock_location on stock_location.sl_id = order_line_item.oli_sl_id
Outer APPLY
(select count(DLI.dli_id) CurLine from delivery_line_item DLI where DLI.dli_dh_id = delivery_header.dh_id and DLI.dli_id <= delivery_line_item.dli_id)
as DelCurLine
Outer APPLY
(select
case when DelCurLine.CurLine <= 4
then '1'
Else
Case when DelCurLine.CurLine <= 8
then '2'
Else '3'
End
End +'-'+order_header.oh_order_number as LineOrd) as LineOrder
Outer APPLY
(select convert(varchar(50),convert(decimal(8,0),delivery_line_item.dli_qty))+'x '+order_line_item.oli_description as LineName) as LineName
where
delivery_header.dh_datetime between #DateFrom and #DateTo
and stock_location.sl_id = #StockLoc
and (order_header.oh_order_number = #OrderNo or #AllOrder = 1)
order by
delivery_header.dh_datetime,
delivery_header.dh_number,
order_header.oh_order_number,
order_header_detail.ohd_delivery_name,
order_header_detail.ohd_delivery_address1,
order_header_detail.ohd_delivery_address2,
order_header_detail.ohd_delivery_address3,
order_header_detail.ohd_delivery_town,
order_header_detail.ohd_delivery_county,
order_header_detail.ohd_delivery_postcode,
order_header_detail.ohd_delivery_country
You can use ROW_NUMBER() with a division by 4. This truncate the decimal because numerator is an interger. This give you group number with a maximum of four row in each group. You can then adjust your query to use this group number in a "group by" clause to return grouped rows into a single one.
Exemple here :
SELECT RawData.BoxGroup,
MIN(dh_datetime),
MIN(dh_number),
MIN(order_header.oh_order_number) as 'Order No'
--And so on
FROM
(SELECT BoxGroup = (ROW_NUMBER() OVER(ORDER BY (SELECT 1)) - 1) / 4,
*
FROM [TableNameOrQuery]) AS RawData
GROUP BY RawData.BoxGroup
Hope this help.

Acting on sql result row count

I'm starting with this query:
SELECT TOP 1 Parties.FirstName + ' ' + Parties.MiddleName + ' ' + Parties.LastName AS Plaintiffs
FROM Jackets INNER JOIN
JacketPartyLinks ON Jackets.Id = JacketPartyLinks.Jacket_JacketPartyLink INNER JOIN
Parties ON Parties.Id = JacketPartyLinks.JacketPartyLink_Party
WHERE (Jackets.Id = #JacketID) AND (JacketPartyLinks.Role = 0)
What I'd like to accomplish is to take a count of what's returned, and if it's one row, do nothing. If it's more than one row, add something like ", et al". Notice that this query returns a single column, and it while it should be able to run standalone, it also needs to work as a subquery.
How can this be done?
EDIT: Add TOP 1
Use top and count..over and then wrap it in an outer query, like so:
select
Plantiffs + case when PlantiffCount > 1 then ', et al' else '' end as Plantiffs
from
(
SELECT TOP 1
Parties.FirstName + ' ' + Parties.MiddleName + ' ' + Parties.LastName as Plantiffs,
count(1) over () as PlantiffCount
FROM
Jackets
INNER JOIN JacketPartyLinks ON
Jackets.Id = JacketPartyLinks.Jacket_JacketPartyLink
INNER JOIN Parties ON
Parties.Id = JacketPartyLinks.JacketPartyLink_Party
WHERE
(Jackets.Id = #JacketID) AND (JacketPartyLinks.Role = 0)
order by Parties.LastName asc
) x
The over () part after count tells it to count all rows and not group by anything. This is why there isn't a group by clause in that query. You can use over with any aggregate function, which is pretty nifty.