Making a case statement when a count condition is bigger than 0 - sql

I want to make a pretty complex SQL query, which results in 0/1 or TRUE/FALSE depending on my conditions.
Idea is that I have a customer table, and then I want to make a couple of checks in different tables, and then if either is true, then we return TRUE, and if not, FALSE:
I was thinking to do this with CASE. See example below:
select
case
((select count(*) from boughtleads bl where bl.customerid = cu.id)>0)
then 'TRUE'
else 'FALSE'
end
from customers cu
left join leadagents la on la.customerid = cu.id
where cu.vatnumber = '30218124'
This returns:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near '>'.
My challenge is then. First of all, the above query fails for some reason, my other problem is I can't use an OR.
My ideal pseudocode would be something like:
select
case
(
((select count(*) from boughtleads bl where bl.customerid = cu.id)>0)
OR
((select count(*) from leadnotifications ln where ln.leadagentid = la.id)>))
then 'TRUE'
else 'FALSE'
end
from customers cu
left join leadagents la on la.customerid = cu.id
where cu.vatnumber = '30218124'
Any ideas on how to attack this?

You are missing when:
select (case when (select count(*) from boughtleads bl where bl.customerid = cu.id) > 0
then 'TRUE'
else 'FALSE'
end)
from customers cu left join
leadagents la
on la.customerid = cu.id
where cu.vatnumber = '30218124';
That said, from a performance perspective it is better to write such conditions using exists:
select (case when exists (select 1 from boughtleads bl where bl.customerid = cu.id)
then 'TRUE'
else 'FALSE'
end)
from customers cu left join
leadagents la
on la.customerid = cu.id
where cu.vatnumber = '30218124'
The count(*) version has to find all matching rows in boughtleads. The exists version can stop at the first match.

Related

Case When + IN combination

I'm a bit stuck in my coding here... I have this extense and complex code, but I'm actually failling by the end of it. I'm getting the SQL Error [42804]: ERROR: argument of CASE/WHEN must be type boolean, not type character varying
The thing, is: when "bairro" matches a row from "SUB_COUNTRY_DB", get psd.name, else get z.name. Any tips on how I could accomplish this?
select distinct
aa.mes,
--aa.modalidade,
--to_date(aa.created_date, 'yyyy-mm-dd') data_captacao,
ucl.ja_comprou_lf as comprou_lf,
case when bairro in (select sub_country from sub_country_DB)
then psd.name
else z.name
end loja,
count (distinct aa.customer_uid) qtde_socios,
count (distinct aa.assinatura_uid) qtde_assinaturas
from assinaturas_ativas aa
left join ultima_compra_loja_fisica ucl on (aa.customer_uid = ucl.customer_uid
and aa.mes = ucl.mes)
left join zip_code z on (aa.customer_uid = z.customer_uid
and aa.mes = z.mes)
left join SUB_COUNTRY_DB psd
on (psd.district = aa.bairro)
group by 1,2,3--,4
Try variants like:
moving condition to an inner query
CASE WHEN EXISTS (SELECT DISTINCT sub_country FROM sub_country_DB WHERE sub_country = barrio)
ANY (PostgreSQL only)
CASE WHEN bairro = ANY(ARRAY_AGG(select sub_country from sub_country_DB))

Sorry I need to hide

Elon Reeve Musk FRS is an entrepreneur and business magnate. He is the founder, CEO, and Chief Engineer at SpaceX; early-stage investor, CEO, and Product Architect of Tesla, Inc.; founder of The Boring Company; and co-founder of Neuralink and OpenAI.
Your inner select returns a table. That can't be used as parameter to match a WHERE IN condition. Instead try using an INNER JOIN
sum(decode(
select sum(dou.noukn)
from dou
join v_kzeiritsu on
dou.zeiritsu = v_kzeiritsu.zeiritsu
)) as noukn2;
Just move your sum logic inside select as follows:
(SELECT SUM(DOU$2.NOUKN)
FROM SDNISHI.V_KZEIRITSU V
WHERE DOU$2.ZEIRITSU = V.ZEIRITSU) AS NOUKN2
In case If it gives aggregation error then use sum(above query) AS NOUKN2
Your code is very strange. For instance, it seems to assume that V_KZEIRITSU has one row. But, you can move this to the FROM clause:
SELECT SUM(CASE WHEN DOU.ZEIRITSU = K.ZEIRITSU THEN DOU.NOUKN ELSE 0 END) AS NOUKN2
FROM DOU LEFT JOIN
V_KZEIRITSU K
ON 1=1 -- in case the table is really empty
A slightly more reasonable version would be:
SELECT SUM(DOU.NOUKN) AS NOUKN2
FROM DOU LEFT JOIN
V_KZEIRITSU K
ON DOU.ZEIRITSU = K.ZEIRITSU -- in case the table is really empty
It seems rather unlikely to me that this is what you really intend. If not, I would suggest that you ask a new question with appropriate same data, desired results, and explanation of the results. A non-working query should not be expected to provide the same level of information.
I'd say that it is, actually, as simple as
select sum(dou.noukn)
from dou
where dou.zeiritsu in (select zeiritsu from v_kzeiritsu)
(I'm not sure what dou is (table? alias?), but I hope you do.)
After you edited the question, I'm editing the answer. I marked with "--> this" two lines that - in my opinion - might help. As previously, the whole sum(case ...) as noukn2 is replaced by a simple sum(dou$2.noukn).
Note that - in Oracle - you can't use as keyword for table alias. For example:
no: from employees as e
yes: from employees e
Here's your query:
SELECT DOU$2.CUSTCD AS CUSTCD,
DOU$2.CHUNO AS CHUNO,
DOU$2.LINNO AS LINNO,
DOU$2.SHIPDAYYM AS SHIPDAYYM,
SUM (DOU$2.NOUKN) AS NOUKN,
SUM (DOU$2.ZEIKN) AS ZEIKN,
SUM (dou$2.noukn) AS noukn2 --> this
FROM SDNISHI.T_HCHUMON_DOUSOU DOU$2
INNER JOIN SDNISHI.SY_KANRI KNR ON KNR.SHIPDAYYM = DOU$2.SHIPDAYYM
INNER JOIN SDNISHI.T_HCHUMON_MEI MEI
ON MEI.CUSTCD = DOU$2.CUSTCD
AND MEI.CHUNO = DOU$2.CHUNO
AND MEI.LINNO = DOU$2.LINNO
AND MEI.SHIPDAYYM = DOU$2.SHIPDAYYM
AND MEI.USEDNGKBN = '0'
AND MEI.CANCELKBN = '0'
LEFT OUTER JOIN SDNISHI.T_HCHUMON_HD HD
ON HD.CUSTCD = MEI.CUSTCD
AND HD.CHUNO = MEI.CHUNO
AND HD.LINNO = MEI.LINNO
AND HD.USEDNGKBN = '0'
AND HD.CANCELKBN = '0'
AND isnull (HD.CANKBN, '00') = '00'
JOIN v_keziritsu vk ON vk.zeiritsu = dou$2.zeiritsu --> this
WHERE DOU$2.USEDNGKBN = '0'
AND DOU$2.CANCELKBN = '0'
AND ( ( MEI.CHGDELKBN = '1'
AND MEI.HDOUSOUKBN = '02'
AND ( MEI.CHUSU > 0
OR MEI.BCHUSU > 0))
OR ( MEI.CHGDELKBN != '1'
AND HD.HDOUSOUKBN = '02'
AND ( MEI.CHKBTNFGA = '1'
AND HD.CHUSU > 0)
OR ( MEI.CHKBTNFGB = '1'
AND HD.BCHUSU > 0)))
GROUP BY DOU$2.CUSTCD,
DOU$2.CHUNO,
DOU$2.LINNO,
DOU$2.SHIPDAYYM

IF THEN in SQL Select Statement

I'm writing a report to return vendor names from journal transactions. Here are the basics of the query below.
I am using data from two views:
Journal detail view = pa_journal_detail
Vendor detail view = ap_vendor
Match on Vendor Number:
The vendor number is contained in the following fields:
ap_vendor.a_vendor_number
pa_journal_detail.jl_ref1 [under certain criteria shown below] ONLY WHEN the journal source code is "API" or "APM"
The source code is stored in the field pa_journal_detail. jl_source_code
The vendor name is stored in the field ap_vendor.a_vendor_name
This is the query I had started with. It is returning incorrect syntax errors when I attempt to run.
SELECT
CASE
WHEN pa_journal_detail. jl_source_code = ‘API’
OR pa_journal_detail. jl_source_code = ‘APM’
THEN(
SELECT a_vendor_name
FROM ap_vendor
INNER JOIN pa_journal_detail
ON pa_journal_detail.jl_ref1 = ap_vendor.a_vendor_number)
ELSE 0
END as a_vendor_name, *
FROM pa_journal_detail
Here is the full query below. I have also tried it with the "TOP 1" included as well. I am now getting the error that there is incorrect syntax near the keyword AS.
SELECT
pa_journal_detail.a_project
,pa_journal_detail.jl_seg2
,pa_journal_detail.jl_seg3
,pa_journal_detail.jl_seg4
,pa_journal_detail.jl_source_code
,pa_journal_detail.jl_ref1
,pa_journal_detail.jl_gl_org
,pa_journal_detail.jl_gl_obj
,pa_journal_detail.jl_line_num
,pa_journal_detail.jl_journal_num
,pa_journal_detail.jl_jnl_year_period
,pa_journal_detail.jl_journal_number
,pa_journal_detail.jl_journal_seq
,(SELECT(CASE
WHEN pa_journal_detail. jl_source_code = 'API'
OR pa_journal_detail. jl_source_code = 'APM'
THEN(
SELECT TOP 1 a_vendor_name
FROM ap_vendor
RIGHT JOIN pa_journal_detail
ON pa_journal_detail.jl_ref1 = ap_vendor.a_vendor_number
)
ELSE 0
END as 'a_vendor_name', *
FROM pa_journal_detail))
FROM pa_journal_detail
This is what I ended up with and its working now! Thank you all!
SELECT
pa_journal_detail.a_project
,pa_journal_detail.jl_seg2
,pa_journal_detail.jl_seg3
,pa_journal_detail.jl_seg4
,pa_journal_detail.jl_source_code
,pa_journal_detail.jl_ref1
,pa_journal_detail.jl_gl_org
,pa_journal_detail.jl_gl_obj
,pa_journal_detail.jl_line_num
,pa_journal_detail.jl_journal_num
,pa_journal_detail.jl_jnl_year_period
,pa_journal_detail.jl_journal_number
,pa_journal_detail.jl_journal_seq
,iif((pa_journal_detail.jl_source_code = 'API'
OR pa_journal_detail.jl_source_code = 'APM')
,(SELECT TOP 1 a_vendor_name
FROM ap_vendor
RIGHT JOIN pa_journal_detail
ON pa_journal_detail.jl_ref1 = ap_vendor.a_vendor_number)
,0) as 'a_vendor_name'
FROM pa_journal_detail
for string compare you need to use single quote
CASE
WHEN pa_journal_detail. jl_source_code = 'API'
OR pa_journal_detail. jl_source_code = 'APM'
THEN(
SELECT top 1 a_vendor_name -- here you need limit or top 1
FROM ap_vendor
INNER JOIN pa_journal_detail
ON pa_journal_detail.jl_ref1 = ap_vendor.a_vendor_number
)
ELSE 0
END as a_vendor_name, *
FROM pa_journal_detail
There is also iif(). I use it far more often than I should, I just like have a tiny little if for simple conditional work instead of big ole' Case statement.
select iif(1 = 1,'True','False')
-- any series that results in a boolean
select iif((1 = 1 and 0 = 0) and (5 / 1 = 5 and 5 % 10 = 5),'True','False')
for your query
SELECT
pa_journal_detail.a_project
,pa_journal_detail.jl_seg2
,pa_journal_detail.jl_seg3
,pa_journal_detail.jl_seg4
,pa_journal_detail.jl_source_code
,pa_journal_detail.jl_ref1
,pa_journal_detail.jl_gl_org
,pa_journal_detail.jl_gl_obj
,pa_journal_detail.jl_line_num
,pa_journal_detail.jl_journal_num
,pa_journal_detail.jl_jnl_year_period
,pa_journal_detail.jl_journal_number
,pa_journal_detail.jl_journal_seq
,iif(pa_journal_detail.jl_source_code = 'API' OR pa_journal_detail.jl_source_code = 'APM',(SELECT TOP 1 a_vendor_name
FROM ap_vendor
RIGHT JOIN pa_journal_detail
ON pa_journal_detail.jl_ref1 = ap_vendor.a_vendor_number)
,0)
'a_vendor_name', *
FROM pa_journal_detail))
FROM pa_journal_detail
I think a case expression may be the wrong tool for the job. If you want to join a table sometimes, a left join may be easier:
SELECT p.*, a.a_vendor_name
FROM pa_journal_detail p
LEFT JOIN ap_vendor a ON p.jl_ref1 = a.a_vendor_number AND
p.jl_source_code IN ('API', 'APM')

How can I refactor this query

How can I rewrite this SQL code? I would like to avoid the repetitive execution of the case for every record.
SELECT chcr.chsid,
CASE
WHEN EXISTS
(SELECT 1
FROM hdr_run chre,
clmerr ce
WHERE chre.chsid = chcr.chsid
AND chre.run_nmbr < chcr.last_run_nmbr
AND chre.clm_error_sid = ce.clm_error_sid
GROUP BY chre.chsid
HAVING COUNT(chre.clm_error_sid) > 0
)
THEN 'Appended'
ELSE 'Never Appended'
END Run_Detail
FROM
clm_res chcr,
clm_der chde
WHERE chde.chsid = chcr.chsid
Have a look at that query please:
SELECT
chcr.chsid,
CASE
WHEN ce.clm_error_sid IS NOT NULL AND COUNT(chre.clm_error_sid, 0) > 0
THEN 'Appended'
ELSE 'Never Appended'
END Run_Detail
FROM
clm_res chcr
JOIN clm_der chde ON chde.chsid = chcr.chsid
LEFT JOIN hdr_run chre ON chre.chsid = chcr.chsid AND chre.run_nmbr < chcr.last_run_nmbr
LEFT JOIN clmerr ce ON chre.clm_error_sid = ce.clm_error_sid
GROUP BY chcr.chsid
If there's more than one clm_der for each clm_res based on their chsid, we'd have to double check that the COUNT is not counting the extras from clm_der.
I have no data to test this on so all I can go on is the SQL you've provided but from a brief look it does not appear that the GROUP BY and HAVING COUNT() > 0 statements are required as the combination of the INNER JOIN condition in the sub-query and the use of EXISTS in the outer query does the same thing.
Does this have exactly the same functionality:
SELECT chcr.chsid,
CASE
WHEN EXISTS
( SELECT 1
FROM hdr_run chre
INNER JOIN
clmerr ce
ON (chre.clm_error_sid = ce.clm_error_sid)
WHERE chre.chsid = chcr.chsid
AND chre.run_nmbr < chcr.last_run_nmbr
)
THEN 'Appended'
ELSE 'Never Appended'
END Run_Detail
FROM clm_res chcr
INNER JOIN
clm_der chde
ON ( chde.chsid = chcr.chsid );
SQLFIDDLE
Also, repeated execution of the CASE statement is not a bad thing as the optimizer should not duplicate executions of the sub-query.

All rows where at least one child has all of its own children pass a condition

I'm having a little trouble with a SQL query, and thought I'd solicit the wisdom of the crowd to see what I'm missing. I'm pretty sure the below works, but it seems really poor and I'm wondering if there's a smarter way (ideally using joins instead of sub-selects) to do this.
The Problem
Let's say I have some tables:
Prize
- PrizeId
RulePrize_Map
- PrizeId
- RuleId
Rule
- RuleId
Conditional
- ConditionalId
- RuleId
- InputId
- ExpectedValue (bit)
Input
- InputId
A Prize is won when at least one Rule is true. A Rule is true when all of its Conditionals are true. A Conditional is "true" when its InputId is either present or not present in the Input table, as designated by the ExpectedValue field. This could perhaps be treated as equivalent to: Count(InputId in Input table) = ExpectedValue for the Conditional's InputId.
Some Examples:
Conditional (InputId = 11, ExpectedValue = 1) -> True if InputId 11 in Input Table
Conditional (InputId = 12, ExpectedValue = 0) -> True if Inputid 12 NOT in Input Table
My Goal
I want to get all Prizes where at least one Rule is "true". I would settle for: "All Rules that are true".
My Attempt
select p.PrizeId from Prize p INNER JOIN RulePrize_Map rpm ON rpm.PrizeId = p.PrizeId
WHERE p.PrizeId IN
(select r.PrizeId from Rule r
where
(select count(*) from Conditional c1 where c1.RuleId = r.RuleId)
=
(select count(*) from Conditional c2
where c2.RuleId = r.RuleId AND
(select count(*) from Input i where i.InputId = c2.InputId) = c2.ExpectedValue
)
)
GROUP BY p.prizeId
The question change a bit, so I have redone the answer...
SELECT
PrizeId
FROM
(
SELECT
PrizeRule_Map.PrizeId,
PrizeRule_Map.RuleId
FROM
PrizeRule_Map
INNER JOIN
Rule
ON Rule.RuleId = PrizeRule_Map.RuleId
INNER JOIN
Conditional
ON Conditional.RuleId = Rule.RuleID
LEFT JOIN
Input
ON Input.InputId = Conditional.InputID
GROUP BY
PrizeRule_Map.PrizeId,
PrizeRule_Map.RuleId
HAVING
COUNT(*) = SUM(CASE Conditional.ExpectedValue
WHEN 1 THEN CASE WHEN Input.InputId IS NULL THEN 0 ELSE 1 END
WHEN 0 THEN CASE WHEN Input.InputId IS NULL THEN 1 ELSE 0 END
END
)
)
AS map
GROUP BY
PrizeId
To get all RuleIds where ALL Conditionals are true:
SELECT r.RuleID
FROM Rule r
JOIN Conditional c
ON c.RuleId = r.RuleId
LEFT JOIN Input i
ON i.InputId = c.InputId
GROUP BY r.RuleID
HAVING COUNT( CASE WHEN (c.ExpectedValue=1) AND (i.InputId IS NOT NULL)
OR (c.ExpectedValue=0) AND (i.InputId IS NULL)
THEN 1
ELSE NULL
END )
= COUNT( * )
Another way - probably slower but it doesn't hurt to test for speed. It doesn't use CASE but the difference (EXCEPT) of two JOINs, only one of them using GROUP BY:
SELECT r.RuleID
FROM Rule r
JOIN Conditional c
ON c.RuleId = r.RuleId
LEFT JOIN Input i
ON i.InputId = c.InputId
WHERE c.ExpectedValue = 1
GROUP BY r.RuleID
HAVING COUNT( i.InputId ) = COUNT( * )
EXCEPT
SELECT r.RuleID
FROM Rule r
JOIN Conditional c
ON c.RuleId = r.RuleId
JOIN Input i
ON i.InputId = c.InputId
WHERE c.ExpectedValue = 0
Try the following:
SELECT Rule.RuleId, Rule.RuleName
FROM Rule
INNER JOIN Conditional ON Rule.RuleId = Conditional.RuleId
Where Conditional.ExpectedValue == true