use distinct within case statement - sql

I have a query that uses multiple left joins and trying to get a SUM of values from one of the joined columns.
SELECT
SUM( case when session.usersessionrun =1 then 1 else 0 end) new_unique_session_user_count
FROM session
LEFT JOIN appuser ON appuser.appid = '6279df3bd2d3352aed591583'
AND appuser.userid = session.userid
LEFT JOIN userdevice ON userdevice.appid = '6279df3bd2d3352aed591583'
AND userdevice.userid = appuser.userid
WHERE session.appid = '6279df3bd2d3352aed591583'
AND (session.uploadedon BETWEEN '2022-04-18 08:31:26' AND '2022-05-18 08:31:26')
But this obviously gives a redundant session.usersessionrun=1 counts since it's a joined resultset.
Here the logic was to mark the user as new if the sessionrun for that record is 1.
I grouped by userid and usersessionrun and it shows that the records are repeated.
userid. sessionrun. count
628212 1 2
627a01 1 4
So what I was trying to do was something like
SUM(CASE distinct(session.userid) AND WHEN session.usersessionrun = 1 THEN 1 ELSE 0 END) new_unique_session_user_count
i.e. for every unique user count, session.usersessionrun = 1 should only be done once.

As you have discovered, JOIN operations can generate combinatorial explosions of data.
You need a subquery to count your sessions by userid. Then you can treat the subquery as a virtual table and JOIN it to the other tables to get the information you need in your result set.
The subquery (nothing in my answer is debugged):
SELECT COUNT(*) new_unique_session_user_count,
session.userid
FROM session
WHERE session.appid = '6279df3bd2d3352aed591583'
AND session.uploadedon BETWEEN '2022-04-18 08:31:26'
AND '2022-05-18 08:31:26'
AND session.usersessionrun = 1
AND session.appid = '6279df3bd2d3352aed591583'
GROUP BY userid
This subquery summarizes your session table and has one row per userid. The trick to avoiding JOIN-created combinatorial explosions is using subqueries that generate results with only one row per data item mentioned in a JOIN's ON-clause.
Then, you join it with the other tables like this
SELECT summary.new_unique_session_user_count
FROM (
SELECT COUNT(*) new_unique_session_user_count,
session.userid
FROM session
WHERE session.appid = '6279df3bd2d3352aed591583'
AND session.uploadedon BETWEEN '2022-04-18 08:31:26'
AND '2022-05-18 08:31:26'
AND session.usersessionrun = 1
AND session.appid = '6279df3bd2d3352aed591583'
GROUP BY userid
) summary
JOIN appuser ON appuser.appid = '6279df3bd2d3352aed591583'
AND appuser.userid = summary.userid
JOIN userdevice ON userdevice.appid = '6279df3bd2d3352aed591583'
AND userdevice.userid = appuser.userid
There may be better ways to structure this query, but it's hard to guess at them without more information about your table definitions and business rules.

Related

MS SQL count values for per row for per id

I'm trying to count incident types per depot. My current query returns the following result set:
I then apply a count query on this set which looks like this:
select middleList.DepotName as depot,
count(middleList.NearMiss) as NearMiss,
count(middleList.Theft) as Theft,
count(middleList.Security) as Security,
count(middleList.LostTimeDisablingInjury) as LostTimeDisablingInjury,
count(middleList.Fire) as Fire,
count(middleList.OccupationalIllness) as OccupationalIllness,
count(middleList.VehicleAccident) as LostTimeDisablingInjury,
count(middleList.Spliiage) as Fire,
count(middleList.PropertyDamage) as OccupationalIllness,
count(middleList.NonConformance) as NonConformance,
count(middleList.Other) as Other
from
(SELECT dbo.IncidentTypes.NearMiss, dbo.IncidentTypes.Theft,
dbo.IncidentTypes.Security, dbo.IncidentTypes.LostTimeDisablingInjury,
dbo.IncidentTypes.Fire, dbo.IncidentTypes.OccupationalIllness,
dbo.IncidentTypes.VehicleAccident, dbo.IncidentTypes.Spliiage,
dbo.IncidentTypes.PropertyDamage, dbo.IncidentTypes.NonConformance,
dbo.IncidentTypes.Other, dbo.Incidents.IncidentTitle, dbo.Depots.DepotName,
dbo.Incidents.IncidentNumber
FROM dbo.Departments INNER JOIN
dbo.Depots ON dbo.Departments.DepotId = dbo.Depots.DepotId
INNER JOIN
dbo.Employees ON dbo.Departments.DepartmentId =
dbo.Employees.DepartmentId INNER JOIN
dbo.IncidentTypes INNER JOIN
dbo.Incidents ON dbo.IncidentTypes.IncidentTypeId =
dbo.Incidents.IncidentTypeId ON dbo.Employees.EmployeeId =
dbo.Incidents.EmployeeId_EmployeeInvolv
Where Incidents.DateCreated = (SELECT MAX (i2.DateCreated) FROM
Incidents as i2 WHERE Incidents.IncidentNumber = i2.IncidentNumber)) as
middleList
group by middleList.DepotName
Which return the following incorrect results:
Please help.
Okay so I found the problem.
It seems like the rows are being counted and not the actual values, thus adding this to the count insured that they were being counted properly:
count(case when middleList.NearMiss <> '0' then 0 end ) as NearMiss
End result being:

SQL query with GROUP BY and HAVING COUNT(condition) in ORACLE

I have three tables: temp, product and feed.
I'll show on example:
select ri.id from temp ri
inner join product i on ri.id = to_char(i.val)
inner join feed f on f.product_id = i.product_id
where i.status = 'Finished'
and f.type = 'Type'
group by f.feed_id, ri.id
having COUNT(f.status = 'SUCCESS') < 1;
so I tried to get all ids from temp that have f.type = 'Type'. Problem is that for one feed.feed_id can be many rows because I could retrigger it 5 times and let's say 4 times it crashed but at 5th attempt it was SUCCESS, so for one feed.feed_id I would have 5 rows and only one would be with f.status = SUCCESS.
Error which I receive for this query is ORA-00907: missing right parenthesis which makes me totally confused.
feed table:
feed_id, status, type
I am interested in all feed_id which don't have even one status='SUCCESS' for type='TYPE'
You can't COUNT a boolean expression in Oracle, you can use a CASE expression instead e.g.
HAVING COUNT(CASE WHEN f.status = 'SUCCESS' THEN 1 END) < 1
This expression returns NULL when the condition is false, so it will only count the rows for which the condition is true (since COUNT of an expression ignores NULL values).
Note also (as #GordonLinoff points out in the comments) that since COUNT cannot return a negative number, it is cleaner (and would be more efficient) to simply compare the result for equality with 0, rather than being less than 1:
HAVING COUNT(CASE WHEN f.status = 'SUCCESS' THEN 1 END) = 0

Count joined table

I have two tables tblFuneralHomes and tblFuneralTransactions. One table may has many transactions. I need to return user's funeralHomes and count transactions, but if funeralHomes has not transactions, it's not returned in result.
Select tfh.funeral_home_guid, tfh.funeral_home_name, tfh.reference_key, count(tft.id) as counts from tblFuneralHomes tfh
inner join tblFuneralTransactions tft on tft.funeral_home_guid = tfh.funeral_home_guid where (tft.canceled=0 and tft.completed=0)
and tfh.funeral_home_guid in (select funeral_home_guid
from tblDirectorHomes
where director_id = '1789a3ae-95e5-4719-ac09-bd1ada01dd5b')
group by tfh.funeral_home_guid, tfh.funeral_home_name, tfh.reference_key
This is the result without join
I know what current user has 3 funeralHomes.
But when I join transactions and count that, one of those homes hasn't transactions and it not displayed.
You have to use a LEFT JOIN instead of a INNER JOIN so all records of tblFuneralHomes are taken:
SELECT tfh.funeral_home_guid, tfh.funeral_home_name, tfh.reference_key, COUNT(tft.id) AS counts
FROM tblFuneralHomes tfh LEFT JOIN tblFuneralTransactions tft ON tft.funeral_home_guid = tfh.funeral_home_guid
WHERE (tft.funeral_home_guid IS NULL OR (tft.canceled = 0 AND tft.completed = 0))
AND tfh.funeral_home_guid IN (
SELECT funeral_home_guid
FROM tblDirectorHomes
WHERE director_id = '1789a3ae-95e5-4719-ac09-bd1ada01dd5b'
)
GROUP BY tfh.funeral_home_guid, tfh.funeral_home_name, tfh.reference_key
You are using columns of tblFuneralTransactions on the WHERE conditions. In case a record of tblFuneralHomes has no tblFuneralTransactions all column values of the (not available) tblFuneralTransactions record are NULL. So the following condition is false: tft.canceled = 0 AND tft.completed = 0.
To include all records of tblFuneralHomes you need to allow the columns of tblFuneralTransactions to be NULL. So you have to replace this condition
(tft.canceled = 0 AND tft.completed = 0)
to the following
(tft.funeral_home_guid IS NULL OR (tft.canceled = 0 AND tft.completed = 0))
I'd suggest following query (little modification of your query):
Select tfh.funeral_home_guid,
tfh.funeral_home_name,
tfh.reference_key,
sum(case when tft.id is null then 0 else 1 end) as counts
from tblFuneralHomes tfh
left join tblFuneralTransactions tft on tft.funeral_home_guid = tfh.funeral_home_guid
where (tft.canceled=0 and tft.completed=0)
and tfh.funeral_home_guid in (select funeral_home_guid
from tblDirectorHomes
where director_id = '1789a3ae-95e5-4719-ac09-bd1ada01dd5b')
group by tfh.funeral_home_guid, tfh.funeral_home_name, tfh.reference_key
I switched to left join, so not matched funeral homes will still be included in resultset. More over, I handled correpsonding nul values from other tables: when it's null then column should be 0, else 1. Then it's enough to sum those values.

Confused in join query in SQL

The following works:
SELECT IBAD.TRM_CODE, IBAD.IPABD_CUR_QTY, BM.BOQ_ITEM_NO,
IBAD.BCI_CODE, BCI.BOQ_CODE
FROM IPA_BOQ_ABSTRCT_DTL IBAD,
BOQ_CONFIG_INF BCI,BOQ_MST BM
WHERE BM.BOQ_CODE = BCI.BOQ_CODE
AND BCI.BCI_CODE = IBAD.BCI_CODE
AND BCI.STATUS = 'Y'
AND BM.STATUS = 'Y'
order by boq_item_no;
Results:
But after joining many tables with that query, the result is confusing:
SELECT (SELECT CMN_NAME
FROM CMN_MST
WHERE CMN_CODE= BRI.CMN_RLTY_MTRL) MTRL,
RRI.RRI_RLTY_RATE AS RATE,
I.BOQ_ITEM_NO,
(TRIM(TO_CHAR(IBAD.IPABD_CUR_QTY,
'9999999999999999999999999999990.999'))) AS IPABD_CUR_QTY,
TRIM(TO_CHAR(BRI.BRI_WT_FACTOR,
'9999999999999999999999999999990.999')) AS WT,
TRIM(TO_CHAR((IBAD.IPABD_CUR_QTY*BRI.BRI_WT_FACTOR),
'9999999999999999999999990.999')) AS RLTY_QTY,
(TRIM(TO_CHAR((IBAD.IPABD_CUR_QTY*BRI.BRI_WT_FACTOR*RRI.RRI_RLTY_RATE),
'9999999999999999999999990.99'))) AS TOT_AMT,
I.TRM_CODE AS TRM
FROM
(SELECT * FROM ipa_boq_abstrct_dtl) IBAD
INNER JOIN
(SELECT * FROM BOQ_RLTY_INF) BRI
ON IBAD.BCI_CODE = BRI.BCI_CODE
INNER JOIN
(SELECT * FROM RLTY_RATE_INF) RRI
ON BRI.CMN_RLTY_MTRL = RRI.CMN_RLTY_MTRL
INNER JOIN
( SELECT IBAD.TRM_CODE, IBAD.IPABD_CUR_QTY,
BM.BOQ_ITEM_NO, IBAD.BCI_CODE, BCI.BOQ_CODE
FROM IPA_BOQ_ABSTRCT_DTL IBAD,
BOQ_CONFIG_INF BCI,BOQ_MST BM
WHERE
BM.BOQ_CODE = BCI.BOQ_CODE
AND BCI.BCI_CODE = IBAD.BCI_CODE
and BCI.status = 'Y'
and bm.status = 'Y') I
ON BRI.BCI_CODE = I.BCI_CODE
AND I.TRM_CODE = BRI.TRM_CODE
AND BRI.TRM_CODE =4
group by BRI.CMN_RLTY_MTRL, RRI.RRI_RLTY_RATE, I.BOQ_ITEM_NO,
IBAD.IPABD_CUR_QTY, BRI.BRI_WT_FACTOR, I.TRM_CODE, I.bci_code
order by BRI.CMN_RLTY_MTRL
Results:
TRM should be 11 instead of 4 in the first row.
you getting 4 because you use
AND BRI.TRM_CODE =4
if you remove this criter you can get true result
In your first query, both of the rows you've highlighted have BCI_CODE=1866.
In the second query, you are joining that result set with a number of others (which come from the same tables, which seems odd). In particular, you are joining from the subquery to another table using BCI_CODE, and from there to (SELECT * FROM ipa_boq_abstrct_dtl) IBAD. Since both of the rows from the subquery have the same BCI_CODE, they will join to the same rows in the other tables.
The quantity that you are actually displaying in the second query is from (SELECT * FROM ipa_boq_abstrct_dtl) IBAD, not from the other subquery.
Is the problem simply that you mean to select I.IPABD_CUR_QTY instead of IBAD.IPABD_CUR_QTY?
You might find this clearer if you did not reuse the same aliases for tables at multiple points in the query.

Logic for a specific Count

I want to fill a asp:GridView with a result of a query. My query, has to count the number of ocurrences of a specific status_id IF the column 'san_proposta.credenciada_id' is different of the value 10. If the column 'san_proposta.credenciada_id' is equals of the value 10 in anothers rows, I need count all ocurrences of a specific status_id that has the 'credenciada_id' equal 10.
I'm trying the follow code, but I don't know how to do with this specifics count.
SELECT DISTINCT San_Proposta.Imovel_Id, San_Logradouro.Descricao, San_Endereco.Logradouro, San_Imovel.Numero, San_TipoComplemento.Descricao AS Expr1,
San_Imovel.Complemento, San_Imovel.TipoDsc1, San_Transacao.TransacaoSigla, San_Credenciada.Apelido, San_Transacao.Transacao_ID, COUNT(San_Proposta.StatusProposta_Id) AS NumeroProposta
FROM San_Proposta
JOIN San_Imovel
ON San_Proposta.Imovel_Id = San_Imovel.Imovel_Id
JOIN San_Endereco
ON San_Imovel.Endereco_Id = San_Endereco.Endereco_Id
JOIN San_Logradouro
ON San_Endereco.Logradouro_Id = San_Logradouro.Logradouro_Id
JOIN San_TipoComplemento
ON San_Imovel.TipoComplemento_Id = San_TipoComplemento.TipoComplemento_Id
JOIN San_Transacao
ON San_Imovel.Transacao_ID = San_Transacao.Transacao_ID
JOIN San_Credenciada
ON San_Imovel.Credenciada_Id = San_Credenciada.Credenciada_Id
WHERE (San_Imovel.Credenciada_Id = 10 OR San_Proposta.Credenciada_Id = 10)
GROUP BY San_Proposta.Imovel_Id, San_Logradouro.Descricao, San_Endereco.Logradouro, San_Imovel.Numero,
San_TipoComplemento.Descricao, San_Imovel.Complemento, San_Imovel.TipoDsc1, San_Transacao.TransacaoSigla,
San_Credenciada.Apelido, San_Transacao.Transacao_ID, San_Proposta.StatusProposta_Id,
San_Proposta.Credenciada_Id, San_Imovel.Credenciada_Id
ORDER BY San_Proposta.Imovel_Id DESC
Based on what you're saying (if I'm interpreting you correctly) it looks like
COUNT(CASE WHEN San_Proposta.credenciada_id = 10 THEN San_Proposta.StatusProposta_Id ELSE NULL END)
would work for your count.