Running complicated sql query within CASE - sql

I have a new report created in postgres - it should only run if there are 3 completed cases, and then determine an ultimate outcome (pass/fail) based on the individual cases. Depending on if it passes or fails, different information needs to be displayed.
I have the three main queries that I need, but now I'm at a loss of how to combine them into one.
The main query that contains the conditional logic for a case is here:
SELECT CASE
WHEN (SELECT COUNT(*) FROM "Alloc" WHERE "CodeID" = 1 AND "StatusCodeID" IN (2,6)) = 3
THEN
--Determine if case passes based on the 3 individual Assessments
CASE
WHEN (SELECT COUNT(*) FROM "Answer" ans
LEFT JOIN "AnswerOption" ansop ON ansop."AnswerOptionID" = ans."AnswerOptionID"
LEFT JOIN "QuestionTextInstance" qtxti ON qtxti."QuestionTextInstanceID" = ans."QuestionTextInstanceID"
LEFT JOIN "Alloc" aloc ON aloc."AllocID" = qtxti."AllocID"
LEFT JOIN "QuestionText" qtxt ON qtxt."QuestionTextID" = qtxti."QuestionTextID"
LEFT JOIN "Code" bcode ON aloc."CodeID" = bcode."CodeID"
WHERE bcode."CodeID" = 1 AND qtxt."QuestionTextID" = 11 AND ans."Value" = 0) >= 1 --At least 2 must have answered 'Yes'
THEN 'Passed' --Execute 'Pass.sql'
ELSE 'Did NOT Pass' --Execute 'NotPass.sql
END
ELSE 'Report did Not Run'
END
This runs correctly and gives the correct results based on the conditions. However, within the THEN and ELSE blocks on the inner CASE statement, I need to display different information that includes many columns and many joins (which I currently have in different .sql files) instead of Pass/Did NOT Pass, but I can not find a way to implement this because it seems that any query within THEN or ELSE blocks can only return a single value.
How can I accomplish this?

It can be done in plain SQL in various ways. One is with well known composite / row types:
SELECT (x).*
FROM (
SELECT CASE WHEN cond_a_here THEN
(SELECT t FROM t WHERE x = 1)
ELSE (SELECT t FROM t WHERE x = 2) END AS x
) sub
Note the parentheses in (x).*. Those are required for a composite type to make the syntax unambiguous.
It's simpler to understand with PL/pgSQL, but you need to understand how to handle composite types. I have posted many related answers ...
CREATE OR REPLACE FUNCTION foo_before()
RETURNS SETOF t AS
$func$
BEGIN
IF cond_a_here THEN
RETURN QUERY
SELECT * FROM t WHERE x = 1;
ELSE
RETURN QUERY
SELECT * FROM t WHERE x = 2;
END IF;
END
$func$ LANGUAGE plpgsql;

Related

using case statement in a where clause

Hello I am missing something because my code errors.
select * from ##ScheduleDetail SD
left join ##HolidayFilterTbl HF on SD.Scheduledate = HF.Testdate
where (ScheduleDate = testdate)
and
(Case
when HF.IsHoliday = 1 then (overtime = 1 and makeup = 0)
else
(overtime = 0 and Makeup = 0)
end
)
and
DOW = 5
order by ActivityStartTime
I've attempted several combinations and each one errors at either the first equal sign or the second. What am I missing?
The branches of a case expression can only return values, not additional expressions to be evaluated in the where condition. You could, however, simulate this behavior with the and and or logical operators:
select *
from ##ScheduleDetail SD
left join ##HolidayFilterTbl HF on SD.Scheduledate = HF.Testdate
where (ScheduleDate = testdate) and
((HF.IsHoliday = 1 and overtime = 1 and makeup = 0) or
(overtime = 0 and Makeup = 0)) and
DOW = 5
order by ActivityStartTime
Note that you have makeup = 0 on both branches of the case expression in the question (or both sides of the or in the answer), so you could extract it out of it and simplify the condition a bit:
select *
from ##ScheduleDetail SD
left join ##HolidayFilterTbl HF on SD.Scheduledate = HF.Testdate
where ScheduleDate = testdate and
makeup = 0 and
((HF.IsHoliday = 1 and overtime = 1) or
overtime = 0) and
DOW = 5
order by ActivityStartTime
If you are still wanting to know how to utilize a CASE Statement Expression in a WHERE Clause the CASE Expression must be compared to a value as that is the syntax understood for conditions contained within a WHERE Clause. See below a mock example.
SELECT *
FROM ##ScheduleDetail SD
LEFT JOIN ##HolidayFilterTbl HF ON SD.Scheduledate = HF.Testdate
WHERE(ScheduleDate = testdate)
AND
/* If you wish to stick with using a CASE Expression within the WHERE Clause set the the CASE Expression equal to 'something'. I usually stick with 1 or 0 'true/false'.
| You simply have to create your own True/False evaluation. You can add your logic checks within a CASE Expression within
| the WHERE Clause and when your logic is TRUE THEN return 1. Basically you are saying when 1 = 1 then return Record.
*/
1 =
Case
WHEN HF.IsHoliday = 1 AND makeup = 0 THEN
CASE WHEN (overtime = 1 OR overtime = 0) THEN 1 END /* Return 1 here to evaluation to TRUE */
ELSE
0 /* You can add another CASE here if needed and when the condition you write in evaluations to 1 'true' return record */
END
AND
DOW = 5
ORDER BY ActivityStartTime;
There are a few reasons I've used CASE Expressions within a WHERE Clause over using AND/ORs. Just one minor reason is it allows me to contain and organize logic in a WHERE Clause inside CASE Expressions rather than having multiple AND/ORs all nested together. I've also found that using CASE Expressions in the WHERE Clause is useful when encountering Dynamic queries that accept variables to be later inserted into the SQL before being sent to the database for processing. In the case of using Dynamic SQL there are times when a CASE Statement MUST be used due to the fact that there could be data that is being compared against in the WHERE clause that is NOT a column.field value but a hardcoded value that is compared to perhaps a user selection or status (as examples)... it might be a static value passed in via the application which is how my web application works that I support which is why I bring it up.
Basically it's good to know how to use a CASE Expression in a WHERE Clause as there are some cases when the ONLY way to evaluate certain data is by using a CASE Expression .
I have no data to test this against and that's not the point. The point of my answer is to simply provide to you an alternative to the existing answer. In my opinion this logic is basic and the already provided answer is the correct one however my answer is to demonstrate how you could go about using a CASE in a WHERE Clause.
If interested see this SO Post for the differences between a CASE Statement vs a CASE Expression however know that this terminology slightly differs between databases.
As an example of this... SQL Server refers to these as Simple vs Searched but refers to all of it as a CASE Expression. Therefore a CASE Expression can either be a Simple or a Searched CASE that can be used within a Statement.

Compare counts between two tables even when one of the tables has no record in oracle

I am stuck in a difficult situation. I need to compare the counts of 2 and update one of the tables based on the comparison. Below are the details:
I have 2 tables: dwh_idh_to_iva_metadata_t and lnd_sml_price_t.
Every day there is a package running that loads data from source table dwh_sml_price_t to destination table lnd_sml_price_t. This package also checks the counts between the tables "dwh_idh_to_iva_metadata_t" and "lnd_sml_price_t". If the counts are matching then it updates few columns in the table dwh_idh_to_iva_metadata_t and if the counts are not matching then the package throws an exception and exits. To do the count comparison we created a cursor and then fetching that cursor for comparison. The code for cursor is as follows:
CURSOR C_CNT_PRICE IS
SELECT
lnd.smp_batchrun_id batch_id,
lnd.lnd_count,
dwh.dwh_count
FROM
(
SELECT
smp_batchrun_id,
COUNT(*) lnd_count
FROM
iva_landing.lnd_sml_price_t
GROUP BY
smp_batchrun_id
) lnd
LEFT JOIN (
SELECT
batchrun_id,
sent_records_count dwh_count,
dwh_sending_table
FROM
dwh.dwh_idh_to_iva_metadata_t
) dwh ON dwh.batchrun_id = lnd.smp_batchrun_id
WHERE
dwh.dwh_sending_table = 'DWH_SML_PRICE_T'
ORDER BY
1 DESC; `
And the actual code for comparison is:
` FOR L_COUNT IN C_CNT_PRICE LOOP --0001,0002
IF L_COUNT.lnd_count = L_COUNT.dwh_count THEN
UPDATE DWH.DWH_IDH_TO_IVA_METADATA_T idh
SET idh.IVA_RECEIVING_TABLE = 'LND_SML_PRICE_T',
idh.RECEIVED_DATE = SYSDATE,
idh.RECEIVED_RECORDS_COUNT = L_COUNT.lnd_count,
idh.status = 'Verified'
WHERE L_COUNT.batch_id = idh.batchrun_id
AND idh.dwh_sending_table = 'DWH_SML_PRICE_T';
COMMIT;
ELSE
RAISE EXCPT_MISSDATA; -- Throw error and exit process immediately
END IF;
END LOOP; `
Now, the problem is that there may be certain cases when the table "LND_SML_PRICE_T" is not having any data, in which case, DWH_IDH_TO_IVA_METDATA_T should have the columns updated as in case of matching counts.
I need help in modifying the code so that comparison is done even in case of no records in LND_SML_PRICE_T table.
Thanks!
If there are no rows in LND_SML_PRICE_T table, cursor C_CNT_PRICE's LND_COUNT equals 0 which means that comparison code should be modified to
FOR L_COUNT IN C_CNT_PRICE LOOP
IF (L_COUNT.lnd_count = L_COUNT.dwh_count)
or L_COUNT.lnd_count = 0 --> this
THEN

IF / Case statment in SQL

I have a column where I have 0 or 1. I like to do the following set up:
If 0 than put / use the Region_table (here I have regions like EMEA, AP,LA with finished goods only) and when it 1 then put / use the Plant_table (here I have plants with non-finished goods) data's.
I tried to write it in 2 different statements but it is not good:
,Case
when [FG_NFG_Selektion] = '0' Then 'AC_region'
End as 'AC_region'
,Case
when [FG_NFG_Selektion] = '1' Then 'AC_plant'
End as 'AC_plant'
I'm not 100% clear on what you're looking for, but if you want to get data from different tables based on the value in the [FG_NFG_Selektion] field, you can do something like this:
SELECT
CASE
WHEN [FG_NFG_Selektion] = '0' THEN r.some_col -- If 0, use value from "region" table
WHEN [FG_NFG_Selektion] = '1' THEN p.some_col -- If 1, use value from "plant" table
END AS new_field
FROM MyTable t
LEFT JOIN AC_region r ON t.pk_col = r.pk_col -- get data from "AC_region" table
LEFT JOIN AC_plant p ON t.pk_col = p.pk_col -- get data from "AC_plant" table
;
If [FG_NFG_Selektion] is a numeric field, then you should remove the single quotes: [FG_NFG_Selektion] = 0.
I would strongly recommend putting the conditions in the ON clauses:
SELECT COALESCE(r.some_col, p.some_col) as som_col
FROM t LEFT JOIN
AC_region r
ON t.pk_col = r.pk_col AND
t.FG_NFG_Selektion = '0' LEFT JOIN
AC_plant p
ON t.pk_col = p.pk_col AND
t.FG_NFG_Selektion = '1';
Why do I recommend this? First, this works correctly if there are multiple matches in either table. That is probably not an issue in this case, but it could be in others. You don't want to figure out where extra rows come from.
Second, putting the conditions in the ON clause allows the optimizer/execution engine to take advantage of them. For instance, it is more likely to use FG_NFG_Selektion in an index.

How can I return a range of values using two drop down parameters in SSRS?

I understand that in order to use drop down parameters in SSRS, a second data set is required.
Here is my main, original query:
--DECLARE #BegSP VARCHAR = 'JC-A'
--DECLARE #EndSP VARCHAR = 'LK-F'
SELECT DISTINCT co.cust_num,
custaddr.name,
co.order_date,
coitem.due_date,
co.Uf_Slsman5,
co.price,
co.co_num,
co.datefld AS Cncl_Date,
CASE
WHEN co.credit_hold = 0
THEN 'No'
ELSE 'Yes'
END AS Credit_Hold
FROM co,
custaddr,
coitem
WHERE co.cust_num = custaddr.cust_num
AND co.cust_seq = custaddr.cust_seq
AND co.co_num = coitem.co_num
AND co.Uf_Slsman5 BETWEEN #BegSP AND #EndSP
AND coitem.stat = 'O'
ORDER BY 7 ASC
This allows me to view a range of SalesPerson numbers by typing the numbers in, however this is not what I want to do.
Obviously, I can't have Declared parameters in that query, because that would negate using an additional query to get the drop-down lists I'm wanting, so I took my declarations out to get this:
SELECT DISTINCT co.cust_num,
custaddr.name,
co.order_date,
coitem.due_date,
co.Uf_Slsman5,
co.price,
co.co_num,
co.datefld AS Cncl_Date,
CASE
WHEN co.credit_hold = 0
THEN 'No'
ELSE 'Yes'
END AS Credit_Hold
FROM co,
custaddr,
coitem
WHERE co.cust_num = custaddr.cust_num
AND co.cust_seq = custaddr.cust_seq
AND co.co_num = coitem.co_num
AND coitem.stat = 'O'
ORDER BY 7 ASC
Ideally, I would like to have two drop down parameter boxes that allow me to return a range of SalesPerson numbers (co.Uf_Slsman5)
This is the second query I would normally use to provide a drop down parameter:
SELECT DISTINCT co.Uf_Slsman5
FROM co
ORDER BY Uf_Slsman5
Herein lies my problem: This query only provides ONE drop down list and only returns ONE SalesPerson number. What can I add/fix to be able to use two drop-down lists, returning a range of SalesPerson numbers?
Any help is greatly appreciated!

Applying the count function within the case function

I am relatively new to SQL and am trying to apply the case function within a view.
While I understand the fundamentals of it, I am having difficulty applying it in the way that I need.
I have 3 columns ApplicationID, ServerName and ServerShared? (true/false).
Each application can have many servers associated to it, while each server only has 1 server type.
I would like to use case to create a further field which can take three values dependent upon whether the values of ServerShared related to an application are all True = Shared, False = Non-shared, Both True and False = Partially shared.
My thoughts were using count function within the case function to set statements where:
if 'count true > 0 and count false > 0' then ServerShared? =
partially if 'count true > 0' and 'count false = 0' then
ServerShared = true and vice versa.
I believe the above logic a way of achieving my result, yet I would appreciate help in both how to structure this within a case statement and any wisdom if there is a better way.
Thanks in advance!
If I get your question right, this should do the trick. Maybe you need to add further columns or adapt the logic. But you should get the logic behind.
SELECT ServerName,
CASE
WHEN COUNT(distinct ServerShared) = 2
THEN N'Server shared'
WHEN MIN(ServerShared) = 0
THEN N'Server not shared'
WHEN MAX(ServerShared) = 1
THEN N'Server shared'
END as ServerShared
FROM myTable
GROUP BY ServerName
There are two main ways to do this problem (super generic answer from non expert :D)
less often executed (one off?), slow execution with potential exponential time increases as rows go up:
This is similar to your suggested solution and involves putting other queries in the Select / field list part of the query - this will get executed for every row returned by the main part of the query (bad news generally speaking):
select
applicationID
, Case (select count * from table as b where a.applicationid = b.applicationid and shareserver=true)
WHEN 0 then 'Non-Shared'
WHEN (select count * from table where a.applicationid = b.applicationid) then 'Shared'
ELSE 'Partially-Shared' END as ShareType
from
tabls as a
get all your data once then perform just the comparison row by row. this is what i would use by default.. its basically better as far as i know but sometimes can be harder to think through.
this line is here to fix formatting issue
select
a.applicationid
,case
when sharedservers = 0 then 'Non-Shared'
when totalservers=sharedservers then 'Shared'
else 'Partially-Shared' END as ShareType
FROM
(select applicationID, count(*) as TotalServers from table) as a
LEFT OUTER JOIN (select applicationID, count(*) as SharedServersfrom table where sharedserver = true) as b
ON a.applicationid=b.applicationid
these queries are just written off the top of my head let me know if there are bug :/
note also the two uses of case statement. one with CASE *value* WHEN *possible value* THEN .. and the second way CASE WHEN *statement that evaluates to boolean* THEN ..