Conditionally Join the Table with a Condition - sql

I have 3 tables. BaseProducts, Products and ProductsMerchants. I need to find the count using a condition. This is my SQL,
ALTER PROCEDURE [dbo].[GetTotalProductsCount]
(
#SuperUser bit,
#MarchantId int
)
AS
BEGIN
IF(#SuperUser = 1)
BEGIN
SELECT COUNT(*) AS Total
FROM [dbo].[BaseProducts]
END
ELSE
BEGIN
SELECT COUNT(*) AS Total
FROM [dbo].[BaseProducts] BP
INNER JOIN [dbo].[Products] P ON P.BaseProductId = BP.Id
INNER JOIN ProductsMerchants PM ON PM.ProductId = P.Id
WHERE PM.MarchantId = #MarchantId;
END
END
The problem is that I need to rewrite the same query just for checking a condition. Can I make it one query?

You can do this:
SELECT COUNT(*) AS Total
FROM [dbo].[BaseProducts]
WHERE #SuperUser = 1
UNION ALL
SELECT COUNT(*) AS Total
FROM [dbo].[BaseProducts] BP
INNER JOIN [dbo].[Products] P ON P.BaseProductId = BP.Id
INNER JOIN ProductsMerchants PM ON PM.ProductId = P.Id
WHERE PM.MarchantId = #MarchantId AND #SuperUser <> 1;
Personally, I find the if form more understandable.
If the inner joins are being used for filtering and don't increase the number of rows, you could also do:
SELECT COUNT(*) AS Total
FROM [dbo].[BaseProducts] BP
LEFT JOIN [dbo].[Products] P ON P.BaseProductId = BP.Id
LEFT JOIN ProductsMerchants PM ON PM.ProductId = P.Id
WHERE PM.MarchantId = #MarchantId OR #SuperUser = 1;
(The PM.MarchantId = #MarchantId undoes the left outer join.)
But once again, I find that the intent of the if is clearer.
Or even this:
SELECT (CASE WHEN #SuperUser = 1 THEN CNT ELSE COUNT(*) END) AS Total
FROM (SELECT COUNT(*) as CNT FROM [dbo].[BaseProducts] BP) const CROSS JOIN
[dbo].[BaseProducts] BP
INNER JOIN [dbo].[Products] P ON P.BaseProductId = BP.Id
INNER JOIN ProductsMerchants PM ON PM.ProductId = P.Id
WHERE PM.MarchantId = #MarchantId OR #SuperUser = 1;

Related

using CTE while declaring variables in SQL

I have two decraed variables and I am trying to set the values from the result of my CTE,
declare #Total_new_claims_received int
declare #Total_Claims_Processed int
End solution I'm looking for is being able to set both declared values from CTE results:
select #Total_new_claims_received = count(id)
from cte
where benefit_code_id not in ('739')
select #Total_Claims_Processed = count(id)
from cte2
where benefit_code_id not in ('739')
Current Code:
declare #Total_new_claims_received int
declare #Total_Claims_Processed int
with cte (ID, Date)
as (
select c.id, c.date
from axiscore.dbo.claim c with (nolock)
inner join claim_line cl on c.Claim_ID = cl.Claim_ID and cl.Linenum = 1
left join axiscore.dbo.member_policy mp with (nolock) on c.Member_Policy_ID = mp.Member_Policy_ID
left join axiscore.dbo.policy p with (nolock) on mp.policy_id = p.policy_id
inner join axiscore.dbo.Claim_Status s with (nolock) on c.Claim_Status_ID = s.Claim_Status_ID
left outer join axiscore.dbo.Claim_Reason_Type r with (nolock) on c.Claim_Reason_Type_ID = r.Claim_Reason_Type_ID
where
c.Updated_Date between '10-1-2019' and '10-31-2019'
and p.Payor_ID = 8
and (c.Claim_Status_ID <> 8)),
cte2 (ID, Date)
as
(
select c.id, c.date
from axiscore.dbo.claim c with (nolock)
inner join claim_line cl on c.Claim_ID = cl.Claim_ID and cl.Linenum = 1
left join axiscore.dbo.member_policy mp with (nolock) on c.Member_Policy_ID = mp.Member_Policy_ID
left join axiscore.dbo.policy p with (nolock) on mp.policy_id = p.policy_id
inner join axiscore.dbo.Claim_Status s with (nolock) on c.Claim_Status_ID = s.Claim_Status_ID
left outer join axiscore.dbo.Claim_Reason_Type r with (nolock) on c.Claim_Reason_Type_ID = r.Claim_Reason_Type_ID
where
c.Updated_Date between '10-1-2019' and '10-31-2019'
and p.Payor_ID = 8
and (c.Claim_Status_ID in (7,6))
and (c.Claim_Reason_Type_ID not in (136,137)))
select #Total_new_claims_received = count(id)
from cte
where benefit_code_id not in ('739')
select #Total_Claims_Processed = count(id)
from cte2
where benefit_code_id not in ('739')
Currently,
it's only setting the value for Total_new_claims_received. It errors out on the second select when I'm setting the value for Total_Claims_Processed. The error is 'invalid object name 'cte2'.
I'm using CTE instead of temp tables becuase I'm calling this proc in a SSIS package. SSIS package doesn't do well with Temp tables. Any other ideas welcome as well.
thanks for your time!
From WITH common_table_expression (Transact-SQL):
A CTE must be followed by a single SELECT, INSERT, UPDATE, or
DELETE statement that references some or all the CTE columns
So define each of your CTEs before each of the select statements that uses it:
declare #Total_new_claims_received int
declare #Total_Claims_Processed int
with cte (ID, Date) as (
select c.id, c.date
from axiscore.dbo.claim c with (nolock)
inner join claim_line cl on c.Claim_ID = cl.Claim_ID and cl.Linenum = 1
left join axiscore.dbo.member_policy mp with (nolock) on c.Member_Policy_ID = mp.Member_Policy_ID
left join axiscore.dbo.policy p with (nolock) on mp.policy_id = p.policy_id
inner join axiscore.dbo.Claim_Status s with (nolock) on c.Claim_Status_ID = s.Claim_Status_ID
left outer join axiscore.dbo.Claim_Reason_Type r with (nolock) on c.Claim_Reason_Type_ID = r.Claim_Reason_Type_ID
where
c.Updated_Date between '10-1-2019' and '10-31-2019'
and p.Payor_ID = 8
and (c.Claim_Status_ID <> 8)
)
select #Total_new_claims_received = count(id)
from cte
where benefit_code_id not in ('739');
with cte2 (ID, Date) as (
select c.id, c.date
from axiscore.dbo.claim c with (nolock)
inner join claim_line cl on c.Claim_ID = cl.Claim_ID and cl.Linenum = 1
left join axiscore.dbo.member_policy mp with (nolock) on c.Member_Policy_ID = mp.Member_Policy_ID
left join axiscore.dbo.policy p with (nolock) on mp.policy_id = p.policy_id
inner join axiscore.dbo.Claim_Status s with (nolock) on c.Claim_Status_ID = s.Claim_Status_ID
left outer join axiscore.dbo.Claim_Reason_Type r with (nolock) on c.Claim_Reason_Type_ID = r.Claim_Reason_Type_ID
where
c.Updated_Date between '10-1-2019' and '10-31-2019'
and p.Payor_ID = 8
and (c.Claim_Status_ID in (7,6))
and (c.Claim_Reason_Type_ID not in (136,137))
)
select #Total_Claims_Processed = count(id)
from cte2
where benefit_code_id not in ('739');

Oracle SQL How to Count Column Value Occurences and Group BY during joins

I'm working on another SQL query, trying to group a collection of records while doing a count and joining tables. See below for goal, current query, and attached scripts for building and populating tables.
Show all customers who have checked more books than DVDs. Display
customer name, total book checkouts and total DVD checkouts. Sort
results by customer first name and last name.
SELECT C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME, COUNT(T.TRANSACTION_ID)
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
INNER JOIN catalog_item CT ON P.CATALOG_ITEM_ID = CT.CATALOG_ITEM_ID
GROUP BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME
ORDER BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME;
Run first: https://drive.google.com/open?id=1PYAZV4KIfZtxP4eQn35zsczySsxDM7ls
Run second: https://drive.google.com/open?id=1pAzWmJqvD3o3n6YJqVUM6TtxDafKGd3f
EDIT
With some help from Mr. Barbaros I've come up with the below query, which is closer. However, this query isn't returning any results for DVDs, which leads me to believe it's a join issue.
SELECT C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME, COUNT(CT1.TYPE) AS BOOK_COUNT, COUNT(CT2.TYPE) AS DVD_COUNT
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
INNER JOIN catalog_item CT1 ON P.CATALOG_ITEM_ID = CT1.CATALOG_ITEM_ID AND CT1.TYPE = 'BOOK'
LEFT OUTER JOIN catalog_item CT2 ON P.CATALOG_ITEM_ID = CT2.CATALOG_ITEM_ID AND CT2.TYPE = 'DVD'
GROUP BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME, CT1.TYPE, CT2.TYPE
ORDER BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME;
Use "conditional aggregates" (use a case expression inside the aggregate function)
SELECT
C.CUSTOMER_FIRSTNAME
, C.CUSTOMER_LASTNAME
, COUNT( CASE WHEN CT.TYPE = 'BOOK' THEN T.TRANSACTION_ID END ) books
, COUNT( CASE WHEN CT.TYPE = 'DVD' THEN T.TRANSACTION_ID END ) dvds
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
INNER JOIN catalog_item CT ON P.CATALOG_ITEM_ID = CT.CATALOG_ITEM_ID
GROUP BY
C.CUSTOMER_FIRSTNAME
, C.CUSTOMER_LASTNAME
HAVING
COUNT( CASE WHEN CT.TYPE = 'BOOK' THEN T.TRANSACTION_ID END )
> COUNT( CASE WHEN CT.TYPE = 'DVD' THEN T.TRANSACTION_ID END )
ORDER BY
C.CUSTOMER_FIRSTNAME
, C.CUSTOMER_LASTNAME
;
You can use catalog_item table twice( think of as seperate tables for books and dvds ), and compare by HAVING clause as :
SELECT C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME,
COUNT(CT1.CATALOG_ITEM_ID) as "Book Checkout",
COUNT(CT2.CATALOG_ITEM_ID) as "DVD Checkout"
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
LEFT JOIN catalog_item CT1 ON P.CATALOG_ITEM_ID = CT1.CATALOG_ITEM_ID AND CT1.TYPE = 'BOOK'
LEFT JOIN catalog_item CT2 ON P.CATALOG_ITEM_ID = CT2.CATALOG_ITEM_ID AND CT1.TYPE = 'DVD'
GROUP BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME
HAVING COUNT(CT1.CATALOG_ITEM_ID) > COUNT(CT2.CATALOG_ITEM_ID)
ORDER BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME;
CUSTOMER_FIRSTNAME CUSTOMER_LASTNAME Book Checkout DVD Checkout
------------------ ----------------- ------------- -------------
Deena Pilgrim 3 1
Emile Cross 5 2
Please try to remove ,CT1.TYPE, CT2.TYPE on your group by clause.

Sum from the different tables Sql server

I have couple of tables which stores amount and I want to group by and get sum - reason for the mutiple tables are nhibernate descriminators.
I am using Union all and works but query is very big.
I am using following query
SELECT CustomerAccountNumber,
vc.CustomerName,
SUM(PermAmount) AS PermAmount,
SUM(FreetextAmount) AS FreetextAmount,
(SUM(PermAmount) + SUM(FreetextAmount)) AS TotalAmountByCustomer
FROM
(
SELECT pp.CustomerAccountNumber,
pl.Amount AS PermAmount,
0 AS FreetextAmount
FROM dbo.PermanentPlacementTransactionLine pl
INNER JOIN dbo.TransactionLine tl ON pl.TransactionLineId = tl.Id
INNER JOIN dbo.PermanentPlacement pp ON pl.PermanentPlacementId = pp.Id
WHERE tl.CurrentStatus = 1
GROUP BY pp.CustomerAccountNumber,
pl.Amount,
tl.Id
UNION ALL
SELECT ft.CustomerAccountNumber,
0 AS PermAmount,
ft.Amount AS FreetextAmount
FROM dbo.FreeTextTransactionLine fttl
INNER JOIN dbo.TransactionLine tl ON fttl.TransactionLineId = tl.Id
INNER JOIN dbo.[FreeText] ft ON fttl.FreeTextId = ft.Id
WHERE tl.CurrentStatus = 1
GROUP BY ft.CustomerAccountNumber,
ft.Amount,
tl.Id
) WIPSummary
INNER JOIN dbo.vw_Customer vc ON WIPSummary.CustomerAccountNumber = vc.CustomerAccount
GROUP BY CustomerAccountNumber,
vc.CustomerName;
is there any elegant way of displaying amount in separate columns ?
I can use partition by if it was same table and want to display row by row.
Try these query, is easy to understand and probably faster than yours.
I assume that the values are unique in your view
WITH cte_a
AS (SELECT pp.customeraccountnumber
,Sum(pl.amount) AS PermAmount
,0 AS FreetextAmount
FROM dbo.permanentplacementtransactionline pl
INNER JOIN dbo.transactionline tl
ON pl.transactionlineid = tl.id
INNER JOIN dbo.permanentplacement pp
ON pl.permanentplacementid = pp.id
WHERE tl.currentstatus = 1
GROUP BY pp.customeraccountnumber),
cte_b
AS (SELECT ft.customeraccountnumber
,0 AS PermAmount
,Sum(ft.amount) AS FreetextAmount
FROM dbo.freetexttransactionline fttl
INNER JOIN dbo.transactionline tl
ON fttl.transactionlineid = tl.id
INNER JOIN dbo.[freetext] ft
ON fttl.freetextid = ft.id
WHERE tl.currentstatus = 1
GROUP BY ft.customeraccountnumber)
SELECT vc.customeraccountnumber
,vc.customername
,Isnull(A.permamount, 0) AS PermAmount
,Isnull(B.freetextamount, 0) AS FreetextAmount
,Isnull(A.permamount, 0)
+ Isnull(B.freetextamount, 0) AS TotalAmountByCustomer
FROM dbo.vw_customer vc
LEFT JOIN cte_a a
ON vc.customeraccount = A.customeraccountnumber
LEFT JOIN cte_b b
ON vc.customeraccount = A.customeraccountnumber
if no table structures and sample data, that is the best I can do to help you.

Too many results in query

I'm fetching some data from our database in MSSQL. Out of this data I want to determine who created the client entry and who took the first payment from this client.
There can be many payment entries for a client on a single booking/enquiry and at the moment, my query shows results for each payment. How can I limit the output to only show the first payment entry?
My query:
SELECT
c.FirstName,
c.LastName,
c.PostalCode,
o.OriginOfEnquiry,
s.SuperOriginName,
c.DateOfCreation,
DATEDIFF(day, c.DateOfCreation, p.DateOfCreation) AS DaysToPayment,
pc.PackageName,
CONCAT(u.FirstName, ' ', u.LastName) AS CreateUser,
(SELECT CONCAT(u.FirstName, ' ', u.LastName)
WHERE u.UserID = p.UserID ) AS PaymentUser
FROM tblBookings b
INNER JOIN tblPayments p
ON b.BookingID = p.BookingID
INNER JOIN tblEnquiries e
ON e.EnquiryID = b.EnquiryID
INNER JOIN tblCustomers c
ON c.CustomerID = e.CustomerID
INNER JOIN tblOrigins o
ON o.OriginID = e.OriginID
INNER JOIN tblSuperOrigins s
ON s.SuperOriginID = o.SuperOriginID
INNER JOIN tblBookingPackages bp
ON bp.bookingID = p.BookingID
INNER JOIN tblPackages pc
ON pc.PackageID = bp.packageID
INNER JOIN tblUsers u
ON u.UserID = c.UserID
WHERE c.DateOfCreation >= '2016-06-01' AND c.DateOfCreation < '2016-06-30'
AND p.PaymentStatusID IN (1,2)
AND e.CustomerID = c.CustomerID
AND p.DeleteMark != 1
AND c.DeleteMark != 1
AND b.DeleteMark != 1
;
I tried adding a "TOP 1" to the nested select statement for PaymentUser, but it made no difference.
you can use cross apply with top 1:
FROM tblBookings b
cross apply
(select top 1 * from tblPayments p where b.BookingID = p.BookingID) as p
Instead of table tblPayments specify sub-query like this:
(SELECT TOP 1 BookingID, UserID, DateOfCreation
FROM tblPayments
WHERE DeleteMark != 1
AND PaymentStatusID IN (1,2)
ORDER BY DateOfCreation) as p
I'm assuming that tblPayments has a primary key column ID. If it is true, you can use this statment:
FROM tblBookings b
INNER JOIN tblPayments p ON p.ID = (
SELECT TOP 1 ID
FROM tblPayments
WHERE BookingID = b.BookingID
AND DeleteMark != 1
AND PaymentStatusID IN (1,2)
ORDER BY DateOfCreation)

using a subquery as a column that's using another column in the 'where' clause

I probably messed that title up! So I have a column called "programID" and I want another column to tell me the most recent date an order was placed using the programID. I think I'd need a subcolumn but the problem is how do I use the row's programID so that it matches the programID for the same row?
DECLARE #ClientIDs varchar(4) = 6653;
DECLARE #ProgramStatus char(1) = 'Y'; -- Y, N, or R
SELECT
rcp.RCPID AS ProgramID
, rcp.RCPName AS Program
, rcp.RCPActive AS ProgramStatus
, aa.AACustomCardInternalReview AS VCCP
, aa.AAJobNo AS AAJobNo
, aas.AASName AS AAStatus
, rcp.RCPOpsApproved AS OpsApproved
, clt.CltID AS ClientID
, rcp.RCPSignatureRequired AS SignatureRequired
, st.STEnumValue AS DefaultShipType
, rcp.RCPShipMethodOverrideType AS ShipTypeOverride
,aa.AANetworkProgramID
,(Select max(cdconfirmationdate) from carddet where ) --can't figure this part out
FROM
RetailerCardProgram rcp WITH (NOLOCK)
INNER JOIN ClientRetailerMap crm WITH (NOLOCK)
ON crm.CRMRetailerID = rcp.RCPRetailerID
INNER JOIN Client clt WITH(NOLOCK)
ON clt.CltID = crm.CRMCltID
LEFT JOIN AssociationApproval aa WITH (NOLOCK)
ON aa.AARetailerID = rcp.RCPRetailerID
AND aa.AABin = rcp.RCPBin6
AND aa.AAFrontOfPlasticTemplateID = rcp.RCPFOCTemplateID
AND aa.AABackOfPlasticTemplateID = rcp.RCPBOCTemplateID
AND ISNULL(aa.AACardID, 0) = ISNULL(rcp.RCPDefaultPlasticCardID, 0)
-- AND LOWER(rcp.RCPName) NOT LIKE '%do not use%' -- Needed for AA Job Number 1594
LEFT JOIN AssociationApprovalStatus aas WITH (NOLOCK)
ON aas.AASID = aa.AAAssociationApprovalStatusID
LEFT JOIN OpenLoopAssociation ola WITH (NOLOCK)
ON ola.OLAID=rcp.RCPOLAID
LEFT JOIN ClientCardProgramMap ccpm WITH (NOLOCK)
ON ccpm.CCPMCardProgramID = rcp.RCPID
AND ccpm.CCPMClientID = clt.CltID
LEFT JOIN TippingModule tm WITH (NOLOCK)
ON tm.TMid = rcp.RCPTippingModuleID
LEFT JOIN GiftCardTemplate fgt WITH (NOLOCK)
ON fgt.gtid = rcp.RCPFOCTemplateID
AND fgt.GTPage='P'
LEFT JOIN GiftCardTemplate bgt WITH (NOLOCK)
ON bgt.gtid = rcp.RCPBOCTemplateID
AND bgt.GTPage='PB'
LEFT JOIN Card c WITH (NOLOCK)
ON c.CardID = rcp.RCPDefaultCarrierID
LEFT JOIN CardType ct WITH (NOLOCK)
ON ct.CTID = c.CardTypeID
LEFT JOIN RetailerCardProgramTCSheetMap rtm1 WITH (NOLOCK)
ON rtm1.RTMRCPID = rcp.RCPID
AND rtm1.RTMInsertOrder = 1
LEFT JOIN RetailerCardProgramTCSheetMap rtm2 WITH (NOLOCK)
ON rtm2.RTMRCPID = rcp.RCPID
AND rtm2.RTMInsertOrder = 2
LEFT JOIN RetailerCardProgramTCSheetMap rtm3 WITH (NOLOCK)
ON rtm3.RTMRCPID = rcp.RCPID
AND rtm3.RTMInsertOrder = 3
LEFT JOIN RetailerCardProgramTCSheetMap rtm4 WITH (NOLOCK)
ON rtm4.RTMRCPID = rcp.RCPID
AND rtm4.RTMInsertOrder = 4
LEFT JOIN TCSheet i1 WITH (NOLOCK)
ON i1.TCSID = rtm1.RTMTCSID
LEFT JOIN TCSheet i2 WITH (NOLOCK)
ON i2.TCSID = rtm2.RTMTCSID
LEFT JOIN TCSheet i3 WITH (NOLOCK)
ON i3.TCSID = rtm3.RTMTCSID
LEFT JOIN TCSheet i4 WITH (NOLOCK)
ON i4.TCSID = rtm4.RTMTCSID
LEFT JOIN ShipType st WITH (NOLOCK)
ON st.STId = rcp.RCPDefaultShipTypeID
WHERE
clt.CltID IN (#ClientIDs) -- 6653 and 6657.
AND rcp.RCPActive IN (#ProgramStatus)
ORDER BY
AAJobNo
, Program
You want to join with a nested select on the table carddet. I'm inferring that RCPID is the relationship between carddet and your main table RetainerCardProgram...
SELECT rcp.RCPID AS ProgramID,
date.MAXDATE AS MaxDate,
rest of your columns...
FROM RetailerCardProgram rcp WITH (NOLOCK)
INNER JOIN (
SELECT RCPID, MAX(cdconfirmationdate) as 'MAXDATE'
FROM carddet
GROUP BY RCPID
) date on date.RCPID = rcp.RCPID
rest of query...
You may want a left join if not all IDs have a date in carddet.
Obligatory addition: Also your use of NOLOCK is a bit terrifying. See http://blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere/