Display rows that have a zero count - sql

I am trying to display rows even if they return a count of zero. However no luck.
I tried using left join.
select
a.Month,
count(b.InsuranceFromJob) [Number of Participants without Insurance]
from
hsAdmin.ReportPeriodLkup a
left join hsAdmin.ClientReport b on
b.ReportPeriod = a.ReportPeriodId
where
b.insurancefromjob = 2 and
a.reportperiodid between (#lastReportId - 11) and #lastReportId
group by
a.Month

Because clientreport is in the where, only rows that exists in clientreport will be in the resultset.
Move the check to the join and you will get the desired result:
select
a.Month,
count(b.InsuranceFromJob) [Number of Participants without Insurance]
from
hsAdmin.ReportPeriodLkup a
left join hsAdmin.ClientReport b on
b.ReportPeriod = a.ReportPeriodId
and b.insurancefromjob = 2
where
a.reportperiodid between (#lastReportId - 11) and #lastReportId
group by
a.Month

Related

SQL Queries Returning Non-equivalent Results and Different Counts Every Run

So I have 2 SQL queries both including the same variable: basically (n_orders and orders_count) should return the same exact results. Problem is:
the 2 columns are not always equivalent for all values
the count of different values changes every run
so first run could be that 20 rows have different (n_orders, orders_count) values then 2nd run says count of different values is 56 for example and so on with changing counts every run.
Query 1:
SELECT product_id,
packing_unit_id,
count(DISTINCT product_sales_order.sales_order_id)
FROM product_sales_order
WHERE product_sales_order.created_at::date BETWEEN '{start}' AND '{end}'
GROUP BY 1, 2
ORDER BY product_id, packing_unit_id
Query 2:
select kpis.*, lr.lr
FROM
(SELECT product_sales_order.product_id,
product_sales_order.packing_unit_id,
count(DISTINCT product_sales_order.sales_order_id) AS orders_count,
count(DISTINCT sales_orders.retailer_id) AS retailers_count,
count(DISTINCT product_sales_order.sales_order_id)*1.0 / count(DISTINCT sales_orders.retailer_id) AS frequency,
(count(DISTINCT sales_orders.retailer_id)*1.0 /(SELECT count(DISTINCT sales_orders.retailer_id) AS month_retailers
FROM sales_orders
JOIN retailers on retailers.id = sales_orders.retailer_id
WHERE sales_orders.created_at::Date BETWEEN '{start}' AND '{end}'
AND sales_orders.sales_order_status_id = 6
AND retailers.is_market_type_private = false)) AS reach,
sum(product_sales_order.total_price) AS nmv,
(sum(product_sales_order.total_price)*1.0 / (SELECT sum(product_sales_order.total_price) AS month_nmv
FROM product_sales_order
WHERE product_sales_order.created_at::Date BETWEEN '{start}' AND '{end}'
AND product_sales_order.purchased_item_count <> 0)) AS contribution,
sum(product_sales_order.purchased_item_count * product_sales_order.basic_unit_count) AS bskt_size,
sum(product_sales_order.total_price)*1.0 / count(DISTINCT product_sales_order.sales_order_id) AS avg_ts,
sum(product_sales_order.total_price)*1.0 / count(DISTINCT sales_orders.retailer_id) AS nmv_p_retailer
FROM product_sales_order
LEFT JOIN sales_orders ON sales_orders.id = product_sales_order.sales_order_id
LEFT JOIN products ON products.id = product_sales_order.product_id
LEFT JOIN retailers on retailers.id = sales_orders.retailer_id
WHERE product_sales_order.created_at::date BETWEEN '{start}' AND '{end}'
GROUP BY 1,2
ORDER BY product_sales_order.product_id, product_sales_order.packing_unit_id, orders_count
) as kpis
LEFT JOIN (
SELECT performance.lost_revenue.product_id,
sum(performance.lost_revenue.lost_revenue) as lr
FROM performance.lost_revenue
WHERE performance.lost_revenue.created_at::Date between '{start}' AND '{end}'
GROUP BY 1
)as lr on lr.product_id = kpis.product_id
What could be corrected regarding the structure of the 2nd SQL query to make it yield the same results for orders_count?
Why does different values count return different results every run?

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)

How to force postgres to return 0 even if there are no rows matching query, using coalesce, group by and join

I've been trying hopelessly to get the following SQL statement to return the query results and default to 0 if there are no rows matching the query.
This is the intended result:
vol | year
-------+------
0 | 2018
Instead I get:
vol | year
-----+------
(0 rows)
Here is the sql statement:
select coalesce(vol,0) as vol, year
from (select sum(vol) as vol, year
from schema.fact_data
join schema.period_data
on schema.fact_data.period_tag = schema.period_data.tag
join schema.product_data
on schema.fact_data.product_tag =
schema.product_data.tag
join schema.market_data
on schema.fact_data.market_tag = schema.market_data.tag
where "retailer"='MadeUpRetailer'
and "product_tag"='FakeProductTag'
and "year"='2018' group by year
) as DerivedTable;
I know the query works because it returns data when there is data. Just doesn't default to 0 as intended...
Any help in finding why this is the case would be much appreciated!
Using your subquery DerivedTable, you could write:
SELECT coalesce(DerivedTable.vol, 0) AS vol,
y.year
FROM (VALUES ('2018'::text)) AS y(year)
LEFT JOIN (SELECT ...) AS DerivedTable
ON DerivedTable.year = y.year;
Remove the GROUP BY (and the outer query):
select 2018 as year, coalesce(sum(vol), 0) as vol
from schema.fact_data f join
schema.period_data p
on f.period_tag = p.tag join
schema.product_data pr
on f.product_tag = pr.tag join
schema.market_data m
on fd.market_tag = m.tag
where "retailer" = 'MadeUpRetailer' and
"product_tag" = 'FakeProductTag' and
"year" = '2018';
An aggregation query with no GROUP BY always returns exactly one row, so this should do what you want.
EDIT:
The query would look something like this:
select v.yyyy as year, coalesce(sum(vol), 0) as vol
from (values (2018), (2019)) v(yyyy) left join
schema.fact_data f
on f.year = v.yyyy left join -- this is just an example. I have no idea where year is coming from
schema.period_data p
on f.period_tag = p.tag left join
schema.product_data pr
on f.product_tag = pr.tag left join
schema.market_data m
on fd.market_tag = m.tag
group by v.yyyy
However, you have to move the where conditions to the appropriate on clauses. I have no idea where the columns are coming from.
From the code you posted it is not clear in which table you have the year column.
You can use UNION to fetch just 1 row in case there are no rows in that table for the year 2018 like this:
select sum(vol) as vol, year
from schema.fact_data innrt join schema.period_data
on schema.fact_data.period_tag = schema.period_data.tag
inner join schema.product_data
on schema.fact_data.product_tag = schema.product_data.tag
inner join schema.market_data
on schema.fact_data.market_tag = schema.market_data.tag
where
"retailer"='MadeUpRetailer' and
"product_tag"='FakeProductTag' and
"year"='2018'
group by "year"
union
select 0 as vol, '2018' as year
where not exists (
select 1 from tablename where "year" = '2018'
)
In case there are rows for the year 2018, then nothing will be fetched by the 2nd query,

Sql Joining 3 Tables Query for Loan Total Result

I have to get the total of Customer Loan from 3 tables the two tables are given loan to sum and the other subtract the paid amount and the tables have Customer ID in common. so far I can get the result only if the Customer ID exist in all tables but if it doesn't exist in one table I won't get Customer in my result. or I get NULL customer IDs when I anchor to the customer.
SELECT
AS1.C_ID AS [Customer ID],
ISNULL(AS1.OldCustomerLoan, 0) AS [Old Loan],
ISNULL(AS2.NewGivenLoan, 0) AS [New Given Loan],
ISNULL(AS3.LoanPaid, 0) AS [PaidLoanAmount],
(ISNULL(AS1.OldCustomerLoan, 0) +
ISNULL(AS2.NewGivenLoan, 0) -
ISNULL(AS3.LoanPaid,0) ) AS Total
FROM
Customer C
LEFT OUTER JOIN
(SELECT
MOC.C_ID,
SUM(MOC.Quantity) AS OldCustomerLoan
FROM
Money_On_Customer MOC (NOLOCK)
GROUP BY
MOC.C_ID) AS1
ON c.C_Id = AS1.C_Id
LEFT OUTER JOIN
(SELECT
NGL.C_ID
,SUM(NGL.G_Take_Loan) AS NewGivenLoan
FROM
Given_Loan NGL
GROUP BY
NGL.C_ID) AS2
ON c.C_Id = AS2.C_Id
LEFT OUTER JOIN
(SELECT
GLP.C_ID, SUM(GLP.G_P_Loan) AS LoanPaid
FROM
Given_Loan_Paid GLP
GROUP BY
GLP.C_ID ) AS3
ON c.C_Id = AS3.C_Id
Here Is a picture of my two results:
When I get NULL Customer IDs
When I don't get All the Customers
You need to use c.c_id for the first column
In order to only get records where they exist in at least one of the tables you can add this to you query, just put you current query in place of the dots, and add the leftid col
Select *
From
(Select c.c_id custonerid,
Coalesce(Coalesce(as1.c_id,as2.c_id),as3.c_id) leftid,......
From ....
) ilv
Where leftid is not null
You might be able to just add
Where coalesce(coalesce(as1.c_id,as2.c_id),as3.c_id) is not null
To then end of your query
#Ab Bennett's answer is right,
because you should use your main(master) table's primary key column
if Customer ID is not available in as1 (Money_On_Customer) it will show null.
Hope you understand my explanation.
UPDATE:
and use following for getting customer id
COALESCE(c.C_Id, AS1.C_Id, AS2.C_Id)
you will get first not null Customer ID
Here is My answer with the help of #Ab Bennett
Select *
From
(
SELECT
C.C_Name AS [Customer ID],
ISNULL(AS1.OldCustomerLoan, 0) AS [Old Loan],
ISNULL(AS2.NewGivenLoan, 0) AS [New Given Loan],
ISNULL(AS3.LoanPaid, 0) AS [PaidLoanAmount],
(ISNULL(AS1.OldCustomerLoan, 0) +
ISNULL(AS2.NewGivenLoan, 0) -
ISNULL(AS3.LoanPaid,0) ) AS Total
FROM
Customer C
LEFT OUTER JOIN
(SELECT
MOC.C_ID,
SUM(MOC.Quantity) AS OldCustomerLoan
FROM
Money_On_Customer MOC (NOLOCK)
GROUP BY
MOC.C_ID) AS1
ON c.C_Id = AS1.C_Id
LEFT OUTER JOIN
(SELECT
NGL.C_ID
,SUM(NGL.G_Take_Loan) AS NewGivenLoan
FROM
Given_Loan NGL
GROUP BY
NGL.C_ID) AS2
ON c.C_Id = AS2.C_Id
LEFT OUTER JOIN
(SELECT
GLP.C_ID, SUM(GLP.G_P_Loan) AS LoanPaid
FROM
Given_Loan_Paid GLP
GROUP BY
GLP.C_ID ) AS3
ON c.C_Id = AS3.C_Id) ilv
Where not ([Old Loan] = 0 and [New Given Loan]=0 and PaidLoanAmount =0 )

SQL Avoid multiplication on inner joins with several returns

OK, not the best title but could not explain it better.
I have a SQL query with a line like this.
count(PRStatusChangesLog.EffectiveMinutes) as timeInHandoverExternal
it works so far but I also want to add something like this
COUNT (distinct a.ActionId) as 'Number Of Actions',
which requires this
INNER JOIN PRAction a on a.PrId = PRHeader.prid
Now the problem which I am sure some of you have already seen. The previous count is now multiplied by the number of actions.
I can see why this happens but I am not sure how best to do this so I can get both the number of actions and the right count without the multiplier.
Simplified full query
SELECT
PRHeader.PrId,
COUNT (distinct a.ActionId) AS 'Number Of Actions',
COUNT (PRStatusChangesLog.EffectiveMinutes) AS timeInHandoverExternal
FROM
PRHeader
LEFT JOIN
PRStatusChangesLog ON PRStatusChangesLog.PrId = PRHeader.PrId
AND PRStatusChangesLog.StatusId = 4100
INNER JOIN
PRAction a ON a.PrId = PRHeader.prid
WHERE
DATEDIFF(mm, prheader.ClosedDate, getdate()) = 1
AND (PRHeader.siteId = 74)
AND prheader.PRTypeId IN (17, 19)
AND PRHeader.tmpStatusId <> 6010
GROUP BY
PRHeader.PrId
You can count a unique column with DISTINCT like COUNT(DISTINCT PRStatusChangesLog.id).
If this is not possible use a subquery for counting the actions. In the SELECT clause you should write something like: (SELECT COUNT(DISTINCT a.ActionId) FROM ... WHERE PRAction a on a.PrId = PRHeader.prid) AS action_count
Using select statement clause in joins to get counts individually then add with final outer select statement.
SELECT
PRHeader.PrId, Count1 'Number Of Actions', Count2 timeInHandoverExternal
FROM
PRHeader
JOIN
(SELECT COUNT (ActionId) Count1
FROM PRAction
GROUP BY PrId) A ON A.PrId = PRHeader.prid
LEFT JOIN
(SELECT
COUNT(PRStatusChangesLog.EffectiveMinutes) Count2, PrId, StatusId
FROM
PRStatusChangesLog
WHERE
StatusId = 4100
GROUP BY
PrId, StatusId) B ON B.PrId = PRHeader.PrId
WHERE
DATEDIFF(mm, prheader.ClosedDate, getdate()) = 1
AND (PRHeader.siteId = 74 )
AND prheader.PRTypeId IN (17,19)
AND PRHeader.tmpStatusId <> 6010