avoid repeating condition in select query - sql

I have the following query to be executed on postgresql
SELECT COUNT(DISTINCT id_client) FROM contract c
INNER JOIN bundle b ON c.bundle_id = b.id
INNER JOIN payment_method pm ON pm.id = c.payment_method_id
WHERE country_id=1 AND b.platform_id=1 AND pm.name <> 'RIB'
AND CONDITION_1
AND id_client NOT IN (
SELECT id_client FROM contract c1
INNER JOIN bundle b1 ON (c1.bundle_id = b1.id)
INNER JOIN payment_method pm1 ON pm1.id = c1.payment_method_id
WHERE c1.country_id=1 AND b1.platform_id=1 AND pm1.name <> 'RIB'
AND CONDITION_2);
I don't like it because it's the same query repeated twice except of CONDITION_1 and CONDITION_2 (and I have another example where it's repeated 3 times).
It's also very slow as well.
I tried to rewrite it as the following:
WITH
filter_cpm AS (
SELECT * FROM contract c
INNER JOIN bundle b ON b.id = c.bundle_id
INNER JOIN payment_method pm ON pm.id = c.payment_method_id
WHERE c.country_id = 1 AND b.platform_id = 1 AND pm.name <> 'RIB'
)
SELECT COUNT(DISTINCT id_client) FROM filter_cpm
WHERE CONDITION_1
AND id_client NOT IN (
SELECT id_client FROM filter_cpm
WHERE CONDITION_2);
Now it's DRY but it's two times slower.
How can I re-write the query to have the same (or better) performance?
EDIT: I cannot join two conditions with AND. For example if CONDITION_1 and CONDITION_2 are VIP, then I want to select clients who were re-qualified from NOT VIP to VIP.

You can select from the common table expression twice, using an outer join:
WITH filter_cpm AS (SELECT *
FROM CONTRACT c
INNER JOIN BUNDLE b
ON b.ID = c.BUNDLE_ID
INNER JOIN PAYMENT_METHOD pm
ON pm.ID = c.PAYMENT_METHOD_ID
WHERE c.COUNTRY_ID = 1 AND
b.PLATFORM_ID = 1 AND
pm.NAME <> 'RIB')
SELECT COUNT(DISTINCT fc1.ID_CLIENT)
FROM filter_cpm fc1
LEFT OUTER JOIN filter_cpm fc2
ON fc2.ID_CLIENT = fc1.ID_CLIENT AND
CONDITION_2
WHERE fc1.CONDITION_1 AND
fc2.ID_CLIENT IS NULL
Best of luck.

Related

Replace correlated subquery with CTE and JOIN

I am trying to rewrite a query which has a correlated subquery, the idea is to replace it with a CTE and join it later.
I have three tables, tbl_transaction, tbl_beneficiaries and tbl_reg_countries. The current (in short) SQL looks like the following.
SELECT
t.USER_ID,
t.TRANSACTION
FROM tbl_transactions t
JOIN tbl_beneficiaries b ON b.ID = t.USER_ID
WHERE b.COUNTRY NOT IN (
SELECT rc.country
FROM tbl_reg_countries rc
WHERE rc.id = t.USER.ID)
My goal is to query only those transactions for each user where the transaction happens outside of the registered countries. So a user may registered X,Y,Z country but had business with Q. In that case only Q should be returned. How could this be replaced with a CTE/JOIN?
I assume both tbl_beneficiaries.COUNTRY and tbl_reg_countries.COUNTRY are not nullable. You can use a LEFT JOIN with NULL test to detect never matching rows
SELECT
t.USER_ID,
t.TRANSACTION
FROM tbl_transactions t
JOIN tbl_beneficiaries b ON b.ID = t.USER_ID
LEFT JOIN tbl_reg_countries rc ON rc.id = t.USER_ID AND b.COUNTRY = rc.country
WHERE rc.country IS NULL
I would try rewriting query with "with"
Like this:
With a As
(Select
Distinct rc.country
From tbl_reg_countries rc
Inner Join tbl_transactions t on rc.id = t.USER.ID
)
Select
t.USER_ID,
t.TRANSACTION
From tbl_transactions t
Inner Join tbl_beneficiaries b On b.ID = t.USER_ID
Where b.COUNTRY Not In (select * from a)

Query the results of another query

I'm writing a query to build an audience for a abandoned cart email. So far, I have the script below, which gives me the correct result (as far as I can tell). However, if possible I would like to query the final result, in order to:
Check if there are multiples of the same email_address that exist in the
output
If true, select the email_address with the oldest (min) modified_date
email_address exist in table a, but I want to use the modified_date from table c.
Can anybody assist me?
Also, I'm not an sql dev, so if you could please explain your answer to help me learn, I would grateful.
Thanks in advance.
SELECT DISTINCT a.guid,
a.customer_id,
a.email_address,
c.product_code,
c.purchase_url,
c.modified_date
FROM (SELECT LIST.guid,
LIST.customer_id,
LIST.email_address
FROM $a$ LIST) a
INNER JOIN (SELECT BASE.guid
FROM $b$ BASE) b
ON a.guid = b.guid
INNER JOIN (SELECT SUPP.customer_id,
SUPP.product_code,
SUPP.purchase_url,
SUPP.modified_date
FROM $c$ SUPP) c
ON a.customer_id = c.customer_id
LEFT JOIN (SELECT EXCL.product_code
FROM $d$ EXCL) d
ON c.product_code = d.product_code
WHERE d.product_code IS NULL
AND c.product_code IS NOT NULL
AND c.modified_date = (SELECT Max(J.modified_date)
FROM $c$ J
WHERE J.customer_id = c.customer_id)
AND Trunc(c.modified_date) = Trunc(sysdate) - 1
If you just need email_address with mininum modified_date you can use below query:
SELECT DISTINCT a.guid,
a.customer_id,
a.email_address,
c.product_code,
c.purchase_url,
min(c.modified_date)over(partition by a.email_address)modified_date
FROM (SELECT LIST.guid,
LIST.customer_id,
LIST.email_address
FROM $a$ LIST) a
INNER JOIN (SELECT BASE.guid
FROM $b$ BASE) b
ON a.guid = b.guid
INNER JOIN (SELECT SUPP.customer_id,
SUPP.product_code,
SUPP.purchase_url,
SUPP.modified_date
FROM $c$ SUPP) c
ON a.customer_id = c.customer_id
LEFT JOIN (SELECT EXCL.product_code
FROM $d$ EXCL) d
ON c.product_code = d.product_code
WHERE d.product_code IS NULL
AND c.product_code IS NOT NULL
AND c.modified_date = (SELECT Max(J.modified_date)
FROM $c$ J
WHERE J.customer_id = c.customer_id)
AND Trunc(c.modified_date) = Trunc(sysdate) - 1

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;

Join SQL query not giving desired result

I have separate queries with me as follows:
SELECT c.id
FROM claim c,
company co,
customer cu
WHERE c.company_id = co.id
AND c.customer_id = cu.id
AND co.company_code = 'LTO'
AND cu.customer_no = '021540'
AND c.invoice_number IS NOT NULL
AND c.invoice_date IS NULL
AND c.invoice_number = '20170331'
SELECT Sum(price)
FROM replaced_part
WHERE claim_id IN ( 628099, 674047, 1182523, 1282549,
1479834, 1480585, 1487452, 1515238 );
SELECT Sum(price)
FROM allowance
WHERE claim_id IN ( 628099, 674047, 1182523, 1282549,
1479834, 1480585, 1487452, 1515238 );
I was trying to group together all the above queries into one using sql equi join as follows:
select co.company_Code,
cu.customer_No,
c.invoice_Number,
sum(r.price),
sum(a.price)
FROM claim c INNER JOIN company co ON c.COMPANY_ID=co.ID
INNER JOIN customer cu ON c.CUSTOMER_ID=cu.ID
INNER JOIN replaced_part r ON r.claim_id=c.id
INNER JOIN allowance a ON a.claim_id = c.id
WHERE co.company_code = 'LTO' and cu.customer_no='021540' and
c.INVOICE_NUMBER is not null and c.INVOICE_DATE is null and
c.INVOICE_NUMBER='20170331'
GROUP BY co.company_Code, cu.customer_No, c.invoice_Number
But I am not getting the desired result(Query execute successfully but the sum is incorrect) as I am getting after running the 3 separate queries defined above... What is the problem here in my query created using equi join??
Relationship between tables are claim ->(one to one) -> company ->(one to one) -> customer ->(one to many) ->Replaced_Part ->(one to many) -> allowance
Please try to use left join for Replaced_Part and allowance
select co.company_Code
, cu.customer_No
, c.invoice_Number
, (SELECT sum(r.price) FROM replaced_part r WHERE r.claim_id = c.id)
, (SELECT sum(a.price) FROM allowance a WHERE a.claim_id = c.id)
FROM claim c
INNER JOIN company co
ON c.COMPANY_ID=co.ID
INNER JOIN customer cu
ON c.CUSTOMER_ID=cu.ID
WHERE co.company_code = 'LTO'
AND cu.customer_no='021540'
AND c.INVOICE_DATE is null
AND c.INVOICE_NUMBER='20170331'
GROUP BY co.company_Code
, cu.customer_No
, c.invoice_Number
try to use temporary table, combine all the tables and make a temporary table, then fire your conditions on that table.
Example :
select co.company_Code,
cu.customer_No,
c.invoice_Number,
sum(r.price),
sum(a.price)
into #temp from (claim c
INNER JOIN company co ON c.COMPANY_ID=co.ID
INNER JOIN customer cu ON c.CUSTOMER_ID=cu.ID
INNER JOIN replaced_part r ON r.claim_id=c.id
INNER JOIN allowance a ON a.claim_id = c.id
and then use your conditions on #temp. like
select * from #temp
WHERE company_code = 'LTO' and customer_no='021540' and
INVOICE_NUMBER is not null and INVOICE_DATE is null and
INVOICE_NUMBER='20170331'
GROUP BY company_Code, customer_No, invoice_Number
Hope this will work for you.

Select qry to using 2 databases

I have the below query:
SELECT
--a.DateEntered,
--a.InventoryId,
a.SKU, a.QtyOnHand,
b.Dateentered AS VDateEntered,
b.GoLive, b.DateOnSite, b.CostPrice,
--a.CurrentPrice,
m.name AS Status,
hrf.category, hrf.department, hrf.BrandedOB, hrf.Division,
hrf.Fascia,
(a.QtyOnHand * b.CostPrice) AS Cost_Value,
NULL AS Item_Quantity, NULL AS Item,
NULL AS Season, hrf.Company,
(a.QtyOnHand * b.CurrentPrice) AS Sellilng_Value,
b.merchandisingseason, b.CostPrice AS Costprice_RP,
b.CurrentPrice, b.InventoryID,
-- a.AverageUnitCost,
-- a.AverageUnitCost AS RP_Stk_AverageUnitCost,
-- a.CurrentPrice AS RP_Stk_Current_Price,
-- a.Statusid,
-- a.Quantity_Sign,
(a.QtyOnHand * b.CostPrice) AS Cost_Value_RP,
(a.QtyOnHand * b.AverageUnitCost) AS AWC_Value
-- a.StockReconciliationId,
-- a.AverageUnitCost,
FROM
[dbo].[FC03QTY] a
JOIN
dbo.inventory b ON a.SKU = b.SKU
LEFT JOIN
(------Hierarchy-------
SELECT
ih.InventoryId, hry.category, hry.department,
hry.BrandedOB, hry.Division, hry.Fascia, hry.Company
FROM
(SELECT
ihn.HierarchyNodeId, ihn.InventoryId
FROM bm.InventoryHierarchyNode IHN
GROUP BY ihn.HierarchyNodeId, ihn.InventoryId) IH
JOIN
(SELECT
g.categoryid, g.category, h.department, i.BrandedOB,
j.Division, K.Fascia, L.company
FROM
Category g (NOLOCK)
JOIN
Department H ON g.departmentid = h.departmentID
JOIN
BrandedOB I (NOLOCK) ON h.BrandedOBID = i.BrandedOBID
JOIN
Division j (NOLOCK) ON i.divisionid = j.divisionid
JOIN
Fascia k (NOLOCK) ON j.fasciaid = k.fasciaID
JOIN
company l (NOLOCK) ON k.companyid = l.companyid
GROUP BY
g.categoryid, g.category, h.department,
i.BrandedOB, j.Division, K.Fascia, L.company) HRY ON ih.HierarchyNodeId = hry.CategoryId
GROUP BY
ih.InventoryId, hry.category, hry.department,
hry.BrandedOB, hry.Division, hry.Fascia, hry.Company) HRF ON b.inventoryid = hrf.inventoryid
JOIN
inventorystatus m (NOLOCK) ON b.statusid = m.statusid
It is using 2 tables -
[dbo].[FC03QTY] a
and
dbo.inventory b
that are joined at the SKU level.
[dbo].[FC03QTY] is on the scratch database and dbo.inventory is on the reports database.
How can I get my query to use these tables if they are on 2 different db?
Any advice greatly received.
In sql server the syntaxis for tables is [database name].[schema name].[table name]
So you need something like this:
SELECT A.*, B.*
FROM
Database1.dbo.Table1 as A,
Database2.dbo.Table2 as B