SQL - Conditional Selection - sql

I am trying to build an Oracle SQL query where I select users that contain at least one License = '+' while all Default Orgs = '-'. In other words, select users with licenses that have no default organizations. In the example below I would expect only Annete to show in the result.
Table_Users: User, License
Table_Organizations: Default_Org, Org_Name
Query below returns no results:
select User
from Table_Users, Table_Organizations
where Table_Users.User = Table_Organizations.UsrX
and (Default_Org = '+' and Default_Org = '-')*

select User
from Table_Users, Table_Organizations
where Table_Users.User = Table_Organizations.UsrX
and License = '+' and Default_Org = '-'

SELECT
u.User
FROM
Table_Users u
INNER JOIN Table_Organizations o
ON u.User = o.Usrx
GROUP BY
u.User
HAVING
COUNT(CASE WHEN License = '+' THEN 1 END) > 0
AND COUNT(CASE WHEN Default_Org = '+' THEN 1 END) = 0
First I would suggest using Explicit not Implicit join, it is a pet peeve of mine and I think a number of others on this site as Explicit join has been part of the ANSI SQL standard for a lot of years now.
As far as what technique you actually want would be called conditional aggregation. By counting only the values you are looking for you can then use them in a HAVING clause to exclude the record(s) you don't want.
Note COUNT(CASE WHEN... THEN END) will work because only the value you want will have a value to be counted and anything not meeting that condition will be NULL and therefore not counted.
Because I don't know which table has License on it you could also potentially use exists as follows:
SELECT
u.User
FROM
Table_Users u
WHERE
u.License = '+'
AND NOT EXISTS(SELECT 1 FROM Table_Organizations o WHERE u.User = o.Usrx AND Default_Org = '+')

Knowing + comes before - we can use a min aggregate. This assumes license and default org could only have '+' or '-' values.
With cte ("user", license, default_org, org_name) as (
SELECT 'Jeff','+','+', 'Org 1' FROM DUAL UNION ALL
SELECT 'Jeff','-','-', 'Org 2' FROM DUAL UNION ALL
SELECT 'Jeff','-','-', 'Org 3' FROM DUAL UNION ALL
SELECT 'Annete','+','-', 'Org 4' FROM DUAL UNION ALL
SELECT 'Annete','-','-', 'Org 5' FROM DUAL)
SELECT "user", min(license), Min(default_org)
FROM CTE A
GROUP BY "user"
HAVING min(license) = '+'
AND min(Default_org) = '-';
If license and default_org both have indexes with user; this would be pretty fast.

Related

IN clause with CASE WHEN in SQL

I try to count the number of times a product is ordered two days in a row, over the last seven days.
I tried to implement an IN condition in a CASE WHEN, but I get the following error:
Comparison operator IN not valid
I'm using DB2
Here is what I've tried :
WITH CTE AS (
SELECT ALLPIC, COUNT(DISTINCT(PAL.CODPRO)) AS NBREFSSTOCKS
FROM FGE50NEUV1.GEPAL AS PAL INNER JOIN FGE50NEUV1.GEPIC AS PIC ON PAL.CODPRO = PIC.CODPRO
GROUP BY ALLPIC
),
CTE2 AS (
SELECT ALLSTS AS ALLPIC, COUNT(DISTINCT CASE WHEN DATPRB1 = ` + dateWMS() + ` THEN CODPRO END) AS NBREFSCDE,
COUNT(
DISTINCT CASE WHEN (
CODPRO IN (SELECT DISTINCT(CODPRO) FROM FGE50NEUV1.GESUPD WHERE DATPRB1 = 20221027)
AND CODPRO IN (SELECT DISTINCT(CODPRO) FROM FGE50NEUV1.GESUPD WHERE DATPRB1 = 20221028)
THEN CODPRO END
)
) AS NBCOMMUNVEILLE,
COUNT(
DISTINCT CASE WHEN (
CODPRO IN (SELECT DISTINCT(CODPRO) FROM FGE50NEUV1.GESUPD WHERE DATPRB1 = 20221026)
AND CODPRO IN (SELECT DISTINCT(CODPRO) FROM FGE50NEUV1.GESUPD WHERE DATPRB1 = 20221027)
THEN CODPRO END
)
) AS NBCOMMUNJM2
FROM FGE50NEUV1.GESUPD AS SUP
GROUP BY ALLSTS
)
SELECT * FROM CTE INNER JOIN CTE2 ON CTE.ALLPIC = CTE2.ALLPIC ORDER BY CTE.ALLPIC
Listing of SQL messages:
SQL0115
Message Text:
Comparison operator &1 not valid.
Cause Text:
Simple comparison operators other than equal and not equal cannot be
used with a list of items. ANY, ALL, and SOME comparison operators
must be followed by a fullselect, rather than an expression or a list
of items. Subqueries cannot be specified in a JOIN condition or in a
CASE expression.
Recovery Text:
Change either the comparison or the
operand. Try the request again.

merge CTE query in Oracle

I am looking for an optimized way to merge CTE query as mentioned below. In the below query, the same table is getting hit twice, I am looking for an option to hit this table once and it will only be possible if I remove CTE and put everything into single query. I am using CTE as I need to derive column value and then compare that value..
Query
with cte as
(
select distinct
'UP' search_update_code,
'G' search_prov_entity_type,
prpr.prpr_id search_prov_id,
prpr.mctn_id search_tax_id,
'UP' group_provdata_upd_cd,
'G' group_provdata_entity,
case
when enroll_type = 'AH' THEN 'A'
when enroll_type = 'VP' THEN 'A'
when enroll_type = 'CK' THEN ' '
end group_provdata_clm_eft_pay_ind <-- deriving values here
from
fsg_prcb_pe_enroll_stream prcb
inner join
cmc_prpr_prov prpr on prpr.mctn_id = prcb.prov_tin_number <-- 1st hit
where
prpr.prpr_entity ='G'
and prcb.prov_tin_number = ?
and prcb.error_flag is null
)
select distinct
cte.*
from cte
inner join cmc_prpr_prov prpr on cte.search_prov_id = prpr.prpr_id <-- 2nd hit
where cte.group_provdata_clm_eft_pay_ind <> prpr.prpr_cl_eft_ind <--- comparing derived values here
and prpr_term_dt >= systimestamp
You can rewrite your query like below:
with cte as
(
select distinct
'UP' search_update_code,
'G' search_prov_entity_type,
prpr.prpr_id search_prov_id,
prpr.mctn_id search_tax_id,
'UP' group_provdata_upd_cd,
'G' group_provdata_entity,
case
when enroll_type = 'AH' THEN 'A'
when enroll_type = 'VP' THEN 'A'
when enroll_type = 'CK' THEN ' '
end group_provdata_clm_eft_pay_ind, <-- deriving values here
prpr.prpr_cl_eft_ind prpr_cl_eft_ind_2,
prpr_term_dt
from
fsg_prcb_pe_enroll_stream prcb
inner join
cmc_prpr_prov prpr on prpr.mctn_id = prcb.prov_tin_number <-- 1st hit
where
prpr.prpr_entity ='G'
and prcb.prov_tin_number = ?
and prcb.error_flag is null
)
select distinct
cte.*
from cte
where group_provdata_clm_eft_pay_ind <> prpr_cl_eft_ind_2 <--- comparing derived values here
and prpr_term_dt >= systimestamp

select statement with subqueries against two databases

I have the below code to show what I am "trying" to accomplish in a stored procedure:
select * from
(
select to_char(sum(aa.amount))
from additional_amount aa, status st
where aa.int_tran_id = st.int_tran_id
and st.stage in ('ACHPayment_Confirmed')
and aa.entry_timestamp > (
select to_date(trunc(last_day(add_months(sysdate,-1))+1), 'DD-MON-RR') AS "day 1"
from dual
)
)
UNION ALL
(
select distinct it.debit_acct as "debit_accounts"
from internal_transactions it
where it.debit_acct IN ( select texe_cnasupro
from service.kndtexe, service.kndtctc
where texe_cncclipu = tctc_cncclipu
and tctc_cntipcli = 'C'
)
)
union all
(select distinct it.credit_acct as "credit_account"
from internal_transactions it
where it.credit_acct IN (select texe_cnasupro
from service.kndtexe, service.kndtctc
where texe_cncclipu = tctc_cncclipu
and tctc_cntipcli = 'C'
)
)
;
Output:
TO_CHAR(SUM(AA.AMOUNT))
----------------------------------------
130250292.22
6710654504
0000050334
2535814905
0007049560
5 rows selected
The top row of the output is what I need in the SP as output based on the below two queries which I am guessing needs to be sub-queried against the top select statement.
The top select is to select the sum of the amount a table with a join against another table for filtering (output:130250292.22).
The second and third selects is actually to check that the accounts in the internal_transactions table are signed up for the corresponding two tables in the service db which is a different db on the same server(owned by the same application).
The tables in the "service" db do not have the same common primary keys as in the first select which is against the same database.
Thank you for your help!
I don't understand your question, but I do know you can simplify this bit:
to_date(trunc(last_day(add_months(sysdate,-1))+1), 'DD-MON-RR') AS "day 1"
to this
trunc (sysdate, 'mm')
and you don't need a SELECT from DUAL to do that either.
and aa.entry_timestamp > trunc (sysdate, 'mm')

Union ALL not giving the column\ names for second group

I am using the tool SQLDBx. which i highly like btw. I have 5 groups of series of Select statements and then I made a UNION ALL after the first group. Now it does run correctly
and displays the output but it does not assign the second group'
s field names.
For example. I am greatly abridging
Completed the SQL's needed to mimic a IBM i display screen. There are 5 product groups making up the screen. I was hoping to have one large Command SQL having the 5 SQLs using UNION ALL. This does 'compile' as a Command. However, it does only brings in the first part fields not the second. So this field is not included in the list of fields tree for COMMAND. PROGR2R2PST,
Is there something not correct how doing the UNION ALL? OUTPUT assigns the column name
of the first group to the second group.
HLDGR1PUN
21454
87273
so if i wanted to have one large SQL with union ALLs, it wont work. is there something other than UNION ALL I should use?
SELECT
count(*) as PROGR1PST,
(
SELECT COALESCE(SUM(OdQty#),0)
FROM ASTCCDTA.OEORH48,ASTCCDTA.TRNSTAT2,ASTDTA.OEORD1
WHERE OHCOM# = TSCOM# AND OHORD# = TSORD# AND OHCOM# = ODCOM# AND OHORD# = ODORD#
AND TSSTAT IN('AEP','BGE')
AND OHORDT IN('RTR','INT','SAM')
AND OHREQD < replace(char(current date, iso), '-', '')
AND OHHLDC = ' '
AND ODPRLC = 'ENG'
AND substr(odprt#,1,5) <> 'NOENG' AND OHORD# in(SELECT a.TSORD# FROM ASTCCDTA.TRNSTAT2 a
WHERE a.tsstat IN('AEP','BGE','EAS','REL','STP'))
) AS PROGR1PUN,
(
SELECT count(*)
FROM ASTCCDTA.OEORH48,ASTCCDTA.TRNSTAT2,ASTCCDTA.OETRA99
WHERE OHCOM# = TSCOM# AND OHORD# = TSORD#
AND (otCOM# = OHCOM# AND OTORD#= OHORD# AND ottrnc = 'AQC')
AND TSSTAT IN('AEP','BGE')
AND OHORDT IN('RTR','INT','SAM')
AND OHREQD = replace(char(current date, iso), '-', '') AND OHHLDC = ' ' AND OHORD# in(SELECT a.TSORD# FROM ASTCCDTA.TRNSTAT2 a
WHERE a.tsstat IN('AEP','BGE','EAS','REL','STP'))
) AS PROGR1TOD,
(
etc..
UNION ALL
SELECT
count(*) as PROGR2R2PST,
(SELECT COALESCE(SUM(OdQty#),0) FROM ASTCCDTA.OEORH48,ASTCCDTA.TRNSTAT2,ASTDTA.OEORD1
WHERE OHCOM# = TSCOM# AND OHORD# = TSORD# AND OHCOM# = ODCOM# AND OHORD# = ODORD#
AND TSSTAT IN('AEP','BGE')
AND OHORDT IN('CUS','CIN','SMC','COC','DON')
AND OHREQD < replace(char(current date, iso), '-', '')
AND OHHLDC = ' '
AND ODPRLC = 'ENG'
AND substr(odprt#,1,5) <> 'NOENG' AND OHORD# in(SELECT a.TSORD# FROM ASTCCDTA.TRNSTAT2 a
WHERE a.tsstat IN('AEP','BGE','EAS','REL','STP'))
) AS PROGR2PUN,
I don't think you can get the result you want by "nesting" the SQL statements and joining them with UNION.
This structure may work, depending upon your requirements:
SELECT 'Name1' AS Label , COUNT(*) AS The_Count
FROM table1
WHERE ...
UNION ALL
SELECT 'PROGR2R2PST', COUNT(*)
FROM table2
WHERE ...
UNION ALL
SELECT 'Name3', COUNT(*)
FROM table3
WHERE ...
This will give you back one row per select:
Label The_Count
--------------------
Name1 45867
PROGR2R2PST 22
Name3 1234
Note that the column names come from the first select. If this format doesn't match your requirements, please be more explicit in the question or in comments and I will try to help.

SQL Query takes to long to return data

Looking for a better way to write this query and my SQL skills aren't great, basic really so looking for any pointers to make this better. This is only the first two columns and the full report will have a further 10.
I'm taking a specific set of repair types and doing analysis on them with counts and calculations. The 1st is jobs brought forward to the current financial year and the second is total amount of jobs currently received.
SELECT
"Type",
(
SELECT
NVL (COUNT(jjo.jjobno), 0)
FROM
jjobh jjo
WHERE
jjo.jclcode = 'L'
AND jjo.jstatus <> '6'
AND jjo.year_rec <> (
SELECT
sub_code
FROM
code_table
WHERE
main_code = 'YEAR'
)
AND (
week_comp IS NULL
OR year_comp = (
SELECT
sub_code
FROM
code_table
WHERE
main_code = 'YEAR'
)
)
AND jjo.jrepair_type = "Type"
) AS "B/F",
(
SELECT
NVL (COUNT(jjo.jjobno), 0)
FROM
jjobh jjo
WHERE
jjo.jclcode = 'L'
AND jjo.jstatus <> '6'
AND jjo.year_rec = (
SELECT
sub_code
FROM
code_table
WHERE
main_code = 'YEAR'
)
AND jjo.jrepair_type = "Type"
) AS "Recvd"
FROM
(
SELECT
rep.repair_type_code AS "Type"
FROM
repair_type rep
WHERE
rep.client = 'L'
AND rep.work_centre = '004682'
ORDER BY
rep.repair_type_code
)
ORDER BY
"Type";
Your code is a mess. I suspect you want something like:
SELECT jjo.jrepair_type, count(*) as valbf
FROM (SELECT coalesce(COUNT(jjo.jjobno), 0)
FROM jjobh jjo cross join
(SELECT sub_code
FROM code_table
WHERE main_code = 'YEAR'
) sc
WHERE jjo.jclcode = 'L' AND
jjo.jstatus <> '6' AND
jjo.year_rec <> sc.sub_code AND
(week_comp IS NULL OR
year_comp = sc.sub_code
)
) jjo join
(SELECT rep.repair_type_code AS "Type"
FROM repair_type rep
WHERE rep.client = 'L' AND
rep.work_centre = '004682'
) rtc
on jjo.jrepair_type = rtc.repair_type_code
group by jjo.jrepair_type;
It looks like you want to join the "jjo" table to the "repair type code" table, producing information about each repair type. The order by in the subquery is useless.
My suggestion is to move the "jjo" table to the outer "from". You should also move the WHERE clauses to the outermost WHERE clause (which I didn't do). I haven't quite figured out the date logic, but this might get you on the right track.