SQL Joins : in, out, shake it all about - sql-server

I am performing the follwing sql to return a data where there is a match of both dob and address in tables1 & 2.
select table1.dob
, table1.address
, sum(case when person_status in ('A','B','C') then 1 else 0 end) as 'ABC_count'
, sum(case when person_status in ('D','E') then 1 else 0 end) as 'DE_Count'
, sum(case when person_status in ('F','G') then 1 else 0 end) as 'FG_Count'
from table1
inner join table2
on (table1.dob = table2.dob and table1.address = table2.address)
where table1.dob > #myDate
group by table1.dob, table1.address
order by table1.dob, table1.address
However I now want to return the data from table1 when there is no match in table2 and only that data, I thought simply changing inner join to left outer would perform what I required, it does not.
Thanks!

If there is no match in the join, the field from the second table are NULL, so you have to check for a NULL value in table2. Assuming dob is NOT NULL in table2, this should solve your problem:
select table1.dob
, table1.address
, sum(case when person_status in ('A','B','C') then 1 else 0 end) as 'ABC_count'
, sum(case when person_status in ('D','E') then 1 else 0 end) as 'DE_Count'
, sum(case when person_status in ('F','G') then 1 else 0 end) as 'FG_Count'
from table1
left outer join table2
on (table1.dob = table2.dob and table1.address = table2.address)
where table1.dob > #myDate and table2.dob is null
group by table1.dob, table1.address
order by table1.dob, table1.address

In this case thre's not a join, you should use NOT EXISTS function.

In my opinion LEFT JOIN is much more cleaner and you should go with that if there is no big difference between the performance of LEFT JOIN and NOT EXISTS. #JNK said "EXISTS and NOT EXISTS are ordinarily faster than joins or other operators like IN because they short circuit - the first time they get a hit they move on to the next entry", but my understanding is that NOT EXISTS and NOT IN are usually expensive as sql server has to go through all the records in the lookup table to make sure that the entry in fact does NOT EXIST, so i dont know how the short circuit would work

You could also use the EXCEPT keyword here.
select table1.dob
, table1.address
, sum(case when person_status in ('A','B','C') then 1 else 0 end) as 'ABC_count'
, sum(case when person_status in ('D','E') then 1 else 0 end) as 'DE_Count'
, sum(case when person_status in ('F','G') then 1 else 0 end) as 'FG_Count'
from table1
where table1.dob > #myDate
EXCEPT
select table1.dob
, table1.address
, sum(case when person_status in ('A','B','C') then 1 else 0 end) as 'ABC_count'
, sum(case when person_status in ('D','E') then 1 else 0 end) as 'DE_Count'
, sum(case when person_status in ('F','G') then 1 else 0 end) as 'FG_Count'
from table1
inner join table2
on (table1.dob = table2.dob and table1.address = table2.address)
where table1.dob > #myDate
That would get you all of the records in the first query that are not in the second query.

Related

How can I combine the below 2 sql queries when there are multiple case statements with SUM involved

This is the first sub-query:
SELECT SUM(CASE WHEN TRADE_TYPE='SELL' THEN (QUANTITY*PRICE) END) -
SUM(CASE WHEN TRADE_TYPE='BUY' THEN (QUANTITY*PRICE) END) AS NET_PL, TRADINGSYMBOL AS STOCK_NAME
FROM dbo.[Table1]
GROUP BY TRADINGSYMBOL, TRADE_DATE
This is the second sub-query:
SELECT SUM(CASE WHEN TRADE_TYPE='BUY' THEN QUANTITY END) -
SUM(CASE WHEN TRADE_TYPE='SELL' THEN QUANTITY END) AS NET_QUANTITY, TRADINGSYMBOL AS STOCK_NAME
FROM dbo.[Table1]
GROUP BY TRADINGSYMBOL
This is the query result of second sub-query:
NET_QUANTITY STOCK_NAME
---------------------- --------------------------------------------------
NULL ABCL
0 ADAT
NULL BAF
NULL BEGE
0 CRECC
NULL CIEN
NULL DFMXA
NULL DFJL
-50 HDANK
1000 MEHD
NULL PRAK
0 TNTS
Every TRADINGSYMBOL that exists as part of second query result WITH NET QUANTITY 0 has to be combined/merged with the first query result. I mean apart from TRADINGSYMBOL(s) included in query-1, the query-2 TRADING SYMBOLS also have to get calculated for their (QUANTITY*PRICE) and should be part of final output.
Please guide me. Thank you.
A quick and easy way would be to just treat them as two sub-queries.
SELECT
PL.STOCK_NAME
,PL.NET_PL
,Q.NET_QUANTITY
FROM
(SELECT SUM(CASE WHEN TRADE_TYPE='SELL' THEN (QUANTITY*PRICE) END) -
SUM(CASE WHEN TRADE_TYPE='BUY' THEN (QUANTITY*PRICE) END) AS NET_PL, TRADINGSYMBOL AS STOCK_NAME
FROM dbo.[Table1]
GROUP BY TRADINGSYMBOL, TRADE_DATE) AS PL
FULL OUTER JOIN
(SELECT SUM(CASE WHEN TRADE_TYPE='BUY' THEN QUANTITY END) -
SUM(CASE WHEN TRADE_TYPE='SELL' THEN QUANTITY END) AS NET_QUANTITY, TRADINGSYMBOL AS STOCK_NAME
FROM dbo.[Table1]
GROUP BY TRADINGSYMBOL) AS Q ON PL.STOCK_NAME = Q.STOCK_NAME

Need to add a subquery in a CASE expression that is part of an aggregate function

I need to add the number of records in which the status is set to 'reopened'. But the 'reopened' status has several IDs.
This is the subquery that will Id the 'reopen' statuses:
SELECT (CASE WHEN s.sr_status_recid = 1 THEN 1 ELSE 0 END) AS Reopened
from v_rpt_service s
where vsrv.sr_status_recid in
(select distinct SR_Status_RecID from SR_Status where [Description] like '%Re-opened%'))
This is the main query that the above query needs to be a part:
SELECT DATEPART(WK, vsrv.date_entered) as WkNumber,
COUNT(vsrv.TicketNbr) AS OpenedIssues, --total ticket count
SUM(CASE WHEN vsrv.Closed_Flag = 1 THEN 1 ELSE 0 END) AS ClosedIssues, --sum of tickets with closed_flag = 1
(SELECT SUM(CASE WHEN s.sr_status_recid = 1 THEN 1 ELSE 0 END)
from v_rpt_service s
where vsrv.sr_status_recid in
(select distinct SR_Status_RecID from SR_Status where [Description] like '%Re-opened%')) AS ReopenedIssues,
SUM(CASE WHEN vsrvy.Surveys_Completed = 1 THEN 1 ELSE 0 END) AS SurveysCompletedWithConnectWise, -- Surveys_Completed flag in view is 1
SUM(CASE WHEN Source = 'Portal' THEN 1 ELSE 0 END) AS IssueLoggedPortal,
SUM(CASE WHEN Source = 'Email Connector' THEN 1 ELSE 0 END) AS IssueLoggedEmai
FROM v_rpt_service vsrv LEFT OUTER JOIN v_rpt_SurveysByTicket vsrvy ON vsrv.TicketNbr = Vsrvy.SR_Service_RecID
WHERE vsrv.company_name <> 'XYZ Test Company' AND vsrv.date_entered BETWEEN '01/01/2016' AND '10/07/2016'
GROUP BY DATEPART(WK, vsrv.date_entered)
ORDER BY WkNumber
How can I have a subquery that uses a CASE statement and the CASE statement is aggregated?
You can use CROSS APPLY
SELECT DATEPART(WK, vsrv.date_entered) as WkNumber,
COUNT(vsrv.TicketNbr) AS OpenedIssues, --total ticket count
SUM(CASE WHEN vsrv.Closed_Flag = 1 THEN 1 ELSE 0 END) AS ClosedIssues, --sum of tickets with closed_flag = 1
SUM(CountReopen.YesNo) AS NumberOfReopen,
SUM(CASE WHEN vsrvy.Surveys_Completed = 1 THEN 1 ELSE 0 END) AS SurveysCompletedWithConnectWise, -- Surveys_Completed flag in view is 1
SUM(CASE WHEN Source = 'Portal' THEN 1 ELSE 0 END) AS IssueLoggedPortal,
SUM(CASE WHEN Source = 'Email Connector' THEN 1 ELSE 0 END) AS IssueLoggedEmail
FROM v_rpt_service vsrv
LEFT OUTER JOIN v_rpt_SurveysByTicket vsrvy
ON vsrv.TicketNbr = Vsrvy.SR_Service_RecID
CROSS APPLY (
SELECT IIF(COUNT(*) > 0,1,0) YesNo
FROM SR_Status
where [Description] like '%Re-opened%'
AND SR_Status_ID = vsrv.sr_status_recid
) CountReopen(YesNo)
WHERE vsrv.company_name <> 'XYZ Test Company'
AND vsrv.date_entered BETWEEN '01/01/2016' AND '10/07/2016'
GROUP BY DATEPART(WK, vsrv.date_entered)
ORDER BY WkNumber

ORA-00937: Not a single-group group function - Query error

Error: ORA-00937: Not a single-group group function
Query:
select count(*) todas,
sum(case when i.prioridade = 1 then 1 else 0 end) urgente,
sum(case when i.prioridade = 2 then 1 else 0 end) alta,
sum(case when i.prioridade = 3 then 1 else 0 end) normal,
sum(case when i.prioridade = 4 then 1 else 0 end) baixa,
(select count(*)
from GMITEMOS i
inner join GMCTLSLA c on c.os = i.cd_numero_os and c.item = i.item
where i.situacao in ('A', 'I', 'P')
and c.ordem = 99999
) naoAvaliados,
sum(case when i.situacao = 'P' then 1 else 0 end) pendentes,
sum(case when i.situacao = 'A' or i.situacao = 'I' then 1 else 0 end) iniciados
from GMITEMOS i
where i.situacao in ('A', 'I', 'P')
and exists (select 1
from GMCTLSLA c
where c.os = i.cd_numero_os
and c.item = i.item)
The error is ocurring here:
(select count(*)
from GMITEMOS i
inner join GMCTLSLA c on c.os = i.cd_numero_os and c.item = i.item
where i.situacao in ('A', 'I', 'P')
and c.ordem = 99999
) naoAvaliados
Can someone tell why is it happening?
You may have fixed it with max but that's not why it's happening and is a little bit hacky. Your problem is that your sub-query, which translates into a single column is not an aggregate query, min, max, sum etc and so needs to be included in a group by clause. You fixed this by wrapping it in max as the maximum of a single value will always be constant.
However, as your sub-query is, itself, an analytic query and will only ever return one row the obvious thing to do is to use a cartesian join to add it to your query. In the explicit join syntax this is known as the cross join.
select count(*) todas
, sum(case when i.prioridade = 1 then 1 else 0 end) urgente
, sum(case when i.prioridade = 2 then 1 else 0 end) alta
, sum(case when i.prioridade = 3 then 1 else 0 end) normal
, sum(case when i.prioridade = 4 then 1 else 0 end) baixa
, naoAvaliados
, sum(case when i.situacao = 'P' then 1 else 0 end) pendentes
, sum(case when i.situacao = 'A' or i.situacao = 'I' then 1 else 0 end) iniciados
from GMITEMOS i
cross join (select count(*) as naoAvaliados
from GMITEMOS j
inner join GMCTLSLA k
on k.os = j.cd_numero_os
and k.item = j.item
where j.situacao in ('A', 'I', 'P')
and k.ordem = 99999
)
where i.situacao in ('A', 'I', 'P')
and exists (select 1
from GMCTLSLA c
where c.os = i.cd_numero_os
and c.item = i.item
)
The cartesian join has a bad reputation as it multiples the number of rows on one side of the join by the number of rows on the other. It does, however, have it's uses, especially in this sort of case.
It's happening because the subquery itself is a scalar result, not a group function. As you have apparently found, you can fix it by substituting a group function that yields an equivalent result to your subquery.
In merge statement, if you are getting this error than simple use the group by and it will resolve the issue.
merge into table1 tb1
using
(select a.id,a.ac_no,sum(a.qy) as qyt,sum(a.amt) as sum_amt from
table2 a, table1 b
where a.id=b.id
and a.id = '1234'
and a.date = '08Oct2014'
and a.ac_no in (123, 234, 345)
and a.ac_no = b.ac_no
group by a.ac_no,a.id
)qry
on (qry.id=tb1.id and qry.ac_no=tb1.ac_no )
when matched then
update set qy=qry.qy,amt = qry.sum_amt;

SQL Server - How to display master details data in columns

I have two tables, to be concise let’s call them TableA and TableB. This is the schema:
TableA
ID – int
Name varchar(50)
TableB
ID – int
TableA_Fk – int
Value varchar(50)
Each record in table A can have at most 9 records in table B. I want to be able to retrieve the data in a columnar form:
TableA-Name, TableB-Value1, … TableB-Value9
Is this possible using queries? Thanks!
You could do something like:
SELECT rank() OVER (ORDER BY tableA_FK) as rank, tableA_fk, value
INTO #temp
FROM TableB b
ORDER BY rank
SELECT a.Name,
CASE WHEN t.rank = 1 THEN t.Value ELSE NULL END AS TableB-Value1,
CASE WHEN t.rank = 2 THEN t.Value ELSE NULL END AS TableB-Value2,
CASE WHEN t.rank = 3 THEN t.Value ELSE NULL END AS TableB-Value3,
.... (etc.)
FROM TableA a
INNER JOIN #temp t ON a.Id = t.tableA_fk
You need Sql Server 2005 or up.
Sorry, but I don't have Sql Server (or the time) to test this well. Hope this gives you an idea and helps.
You will require a LEFT JOIN and a PIVOT table
This should do it, in addition to be DBRM independant.
SELECT A.Name
, SUM(CASE WHEN B.Value = 1 THEN 1 ELSE NULL END) AS B_Value_1
, SUM(CASE WHEN B.Value = 2 THEN 2 ELSE NULL END) AS B_Value_2
, SUM(CASE WHEN B.Value = 3 THEN 3 ELSE NULL END) AS B_Value_3
, SUM(CASE WHEN B.Value = 4 THEN 4 ELSE NULL END) AS B_Value_4
, SUM(CASE WHEN B.Value = 5 THEN 5 ELSE NULL END) AS B_Value_5
, SUM(CASE WHEN B.Value = 6 THEN 6 ELSE NULL END) AS B_Value_6
, SUM(CASE WHEN B.Value = 7 THEN 7 ELSE NULL END) AS B_Value_7
, SUM(CASE WHEN B.Value = 8 THEN 8 ELSE NULL END) AS B_Value_8
, SUM(CASE WHEN B.Value = 9 THEN 9 ELSE NULL END) AS B_Value_9
FROM A
INNER JOIN B ON B.TableA_FK = A.ID
GROUP BY A.Name
ORDER BY A.Name

Implement two filters in one condition - SQL

I am creating a report. In that I am displaying total no of leads, leads sent via Fax and Leads sent via SMS. I wrote a procedure to fetch the records . Here I have to check an condition that both Leads sent via SMS and Leads sent via Fax should not be zero. If both are zero I should not fetch the record. If one of them has any value i should fetch the record. Is it possible implement it through SQL Query.
FROM OP comment to astander
SELECT C.ClientID ,
C.ClientName ,
C.OrganizationName,
C.FirstName ,
S.SMSOverageRate ,
'' as Cost ,
Count(*) as TotalLeads ,
Sum(CASE DeliveryViaFax WHEN 'Y' THEN 1 ELSE 0 END) AS FaxCount ,
Sum(CASE DeliveryViaSMSEmail WHEN 'Y' THEN 1 ELSE 0 END) AS SMSCount
FROM CMN_LeadSaleDetails S INNER JOIN
CMN_LeadClients C ON C.ClientID = S.BuyerID
WHERE C.SellerTenantId = #TenantId
AND S.SellerJournalID = CASE #JournalId WHEN 0 THEN S.SellerJournalID ELSE #JournalId END
GROUP BY C.ClientID ,
C.ClientName ,
C.OrganizationName,
C.FirstName ,
S.SMSOverageRate
You can try something like this
SELECT *
FROM TABLE
WHERE (TotalSMSLeads != 0
OR TotalFaxLeads != 0)
Provide the example query and we can assist you further.
OK, from what you commented i would try something like this
SELECT C.ClientID ,
C.ClientName ,
C.OrganizationName,
C.FirstName ,
S.SMSOverageRate ,
'' as Cost ,
Count(*) as TotalLeads ,
Sum(CASE DeliveryViaFax WHEN 'Y' THEN 1 ELSE 0 END) AS FaxCount ,
Sum(CASE DeliveryViaSMSEmail WHEN 'Y' THEN 1 ELSE 0 END) AS SMSCount
FROM CMN_LeadSaleDetails S INNER JOIN
CMN_LeadClients C ON C.ClientID = S.BuyerID
WHERE C.SellerTenantId = #TenantId
AND S.SellerJournalID = CASE #JournalId WHEN 0 THEN S.SellerJournalID ELSE #JournalId END
GROUP BY C.ClientID ,
C.ClientName ,
C.OrganizationName,
C.FirstName ,
S.SMSOverageRate
HAVING ( Sum(CASE DeliveryViaFax WHEN 'Y' THEN 1 ELSE 0 END) != 0
OR Sum(CASE DeliveryViaSMSEmail WHEN 'Y' THEN 1 ELSE 0 END) != 0)
Alternatively..
Select *
From TABLE
Where (SMSLeads + FaxLeads) > 0
Using your SQL
Select C.ClientID ,C.ClientName ,C.OrganizationName
C.FirstName ,S.SMSOverageRate ,'' as Cost,
Count(*) as TotalLeads ,
Sum(CASE DeliveryViaFax WHEN 'Y' THEN 1 ELSE 0 END) AS FaxCount ,
Sum(CASE DeliveryViaSMSEmail WHEN 'Y' THEN 1 ELSE 0 END) AS SMSCount
FROM CMN_LeadSaleDetails S
INNER JOIN CMN_LeadClients C
ON C.ClientID = S.BuyerID
WHERE C.SellerTenantId = #TenantId
AND S.SellerJournalID = CASE #JournalId WHEN 0 THEN S.SellerJournalID ELSE #JournalId
Group By C.ClientID ,C.ClientName ,C.OrganizationName
C.FirstName ,S.SMSOverageRate
Having (FaxCount+SMSCount) > 0

Resources