PLSQL Case in Where clause for >= (date) - oracle

I am using a table that contains various milestones to determine the stages of a contract. Each milestone has a date, and I need to build a report on a handful of those milestones to determine the average length of time it takes for a project to get from one stage to another. The table contains information spanning from 1996 onward, and I'm only interested in projects that have been finalized (milestone 4.08) in the past year.
The trouble is I can't seem to build a case statement that looks for a particular milestone, and then filters the date that corresponds to it. Below is the code I'm working on.
Select Distinct CONSTRUCTION.CONTRACTS.CONTRACTNUMBER,
CONSTRUCTION.TBL_CONTRACTPROJECTCOMBO.LOCATIONS,
Max(Case
When CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER =
'3.02' Then
CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE
End) RECEIVED_IN_AUDIT,
Max(Case
When CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER =
'4.04' Then
CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE
End) INITIAL_AUDIT_COMPLETE,
Max(Case
When CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER =
'4.08' Then
CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE
End) FINAL_AUDIT_COMPLETE,
(Max(Case
When CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER =
'4.08' Then
CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE
End) - (Max(Case
When CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER =
'3.02' Then
CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE
End))) DAYS_IN_AUDIT,
(Case
When Extract(Month From Max(Case
When CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER =
'4.08' Then
CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE
End)) >= 7 Then
Extract(Year From Max(Case
When CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER =
'4.08' Then
CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE
End)) + 1
Else
Extract(Year From Max(Case
When CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER =
'4.08' Then
CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE
End))
End) FY_AUDIT_COMPLETE
From CONSTRUCTION.CONTRACTS
Join CONSTRUCTION.CONTRACTMILESTONES on CONSTRUCTION.CONTRACTMILESTONES.CONTRACTID = CONSTRUCTION.CONTRACTS.ID
Join CONSTRUCTION.MILESTONEEVENTS on CONSTRUCTION.MILESTONEEVENTS.ID = CONSTRUCTION.CONTRACTMILESTONES.MILESTONEID
Join CONSTRUCTION.TBL_CONTRACTPROJECTCOMBO on CONSTRUCTION.TBL_CONTRACTPROJECTCOMBO.CONTRACTNUMBER = CONSTRUCTION.CONTRACTS.CONTRACTNUMBER
Where (CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER = '4.04' Or
CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER = '4.08' Or
CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER = '3.02')
And (case when CONSTRUCTION.MILESTONEEVENTS.MILESTONENUMBER = '4.08'
then CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE >= sysdate-365 END)
Group By CONSTRUCTION.CONTRACTS.CONTRACTNUMBER,
CONSTRUCTION.TBL_CONTRACTPROJECTCOMBO.LOCATIONS
The error I get is
ORA-00905: missing keyword.
The error indicates that my code breaks between the CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE and the ">=". What am I doing wrong?
Any help is appreciated. Thank you.

Aliases exist for reason; I suggest you use them and make queries you write easier to read.
As of a problem you have: I believe this solves part of it:
SELECT DISTINCT c.contractnumber,
m.milestone,
m.milestonedescription,
cm.milestonedate,
m.milestonenumber
FROM construction.contracts c
JOIN construction.contractmilestones cm ON cm.contractid = c.id
JOIN construction.milestoneevents m ON m.id = cm.milestoneid
WHERE cm.milestonedate >=
CASE WHEN m.milestonenumber = '4.08' THEN TRUNC (SYSDATE) - 365
ELSE DATE '2021-01-01' --> if it isn't 4.08, what should CM.MILESTONEDATE be larger than?
END
CASE you wrote is wrong because it should represent some condition, while your query tries to use it, but in a wrong manner (you can't have THEN within the CASE).
Note ELSE I wrote; I used date literal (Jan 1st 2021) because I don't know what you want to do with other milestones (different from 4.08). If you do (you should, right?), put that date in there.

I guess you have extra bracket here at end..
then CONSTRUCTION.CONTRACTMILESTONES.MILESTONEDATE >= to_date(to_char(sysdate, 'mm/dd/yyyy'), 'mm/dd/yyyy')-365) END)
Can you try removing and executing same query again.

Related

need to use case when for select document path for two type?

HOW TO WRITE QUERY
SELECT DOC_PATH,
(CASE WHEN DOCTYPE=1 THEN SCAN_PATH from documents where DOC_NO='A112' AND ROWNUM=1 END) AS DOCS,
(CASE WHEN DOCTYPE=2 THEN SCAN_PATH from documents where DOC_NO='B331' AND ROWNUM=1 END) AS DOCS
FROM documents
The structure of your query implies that you should be doing an aggregation:
SELECT
DOC_PATH,
MAX(CASE WHEN DOCTYPE = 1 AND DOC_NO = 'A112' THEN SCAN_PATH END) AS DOCS1,
MAX(CASE WHEN DOCTYPE = 2 AND DOC_NO = 'B331' THEN SCAN_PATH END) AS DOCS2
FROM documents
GROUP BY DOC_PATH;

ORACLE - FLATTEN DATA

I am sure that this question has been asked before but possibly not in this way.
Here is the data:
I need the rows flattened and the end output:
Any ideas?
As you already said we exactly can use row_number here and then use the row_number to pivot it,
select *
from
(
select userid,make,model,
row_number() over (partition by userid order by make,model) rn
from table1
)
pivot
(
max(make) make,max(model) model
for rn in (1,2,3,4,5)
)
Or
Using conditional aggregation which many prefers over PIVOT
select userid
,max(case when rn = 1 then make end) make_1
,max(case when rn = 1 then model end) model_1
,max(case when rn = 2 then make end) make_2
,max(case when rn = 2 then model end) model_2
,max(case when rn = 3 then make end) make_3
,max(case when rn = 3 then model end) model_3
from
(
select userid,make,model,
row_number() over (partition by userid order by make,model) rn
from table1
)
group by userid;
In both cases you can say the disadvantage is hard coding the row numbers but this is how it works or else you may opt for dynamic SQL if necessary.

Count and Sum inside a select with multiple sums and counts

I'm using sub-query factoring and I have a query that returns invoice lines, and in the end I have this final sub-query:
I've already tried Partition but without success
SELECT
COUNT(CASE WHEN PC <> 0 THEN 1 END) AS A_LINECOUNT,
SUM(CASE WHEN PC > 0 THEN NR ELSE 0 END) AS B_PRODUCTCOUNT,
COUNT(CASE WHEN ALLOW_PAY = 1 THEN 1 END) AS C_INVOICECOUNT, --- ERROR
SUM(CASE WHEN ALLOW_PAY = 1 THEN MISSING_VALUE ELSE 0 END) AS D_INVOICETOTAL, --- ERROR
COUNT(CASE WHEN IS_NON_LIQUIDABLE_PRODUCT = 1 THEN 1 END) AS E_CONDITIONCOUNT,
COUNT(CASE WHEN IS_LIQUIDABLE_PRODUCT = 1 THEN 1 END) AS F_CONDITIONCOUNT
FROM MAIN_Q
The calculation of C_INVOICECOUNT and D_INVOICETOTAL is not correct because their values are repeated within each line of the invoice. Please consider that um MAIN_Q i also have a document_id where i can group by.
thanks
Maybe I understood correctly, maybe not, but this is too long for comment. If yes, C_INVOICECOUNT can be count as:
count(distinct case when allow_pay = 1 then document_id end)
But the problem is with D_INVOICETOTAL. You have repeated values for each invoice here and details which do not repeat. If so, add row numbering to your query:
select main_q.*, row_number() over (partition by document_id) rn from main_q
and then in problematic places use rn = 1:
select ...
count(case when rn = 1 and allow_pay = 1 then 1 end),
sum(case when rn = 1 and allow_pay = 1 then missing_value else 0 end)
...
from (select main_q.*, row_number() over (partition by document_id) rn from main_q)
Only first rows for each invoice will be analysed. Of course you can add row_number in earlier step.

Input data to complicated join table dbgrid

I have a dbgrid that I am going to use as input method for my application. Query to that goes by this:
select a.tanggal,
(case a.kode
when '01' then
a.jumlah
else
null
end) as SERATUSRIBU,
(case a.kode
when '02' then
a.jumlah
else
null
end) as LIMAPULUHRIBU,
(case a.kode
when '03' then
a.jumlah
else
null
end) as DUAPULUHRIBU,
(case a.kode
when '04' then
a.jumlah
else
null
end) as SEPULUHRIBU,
(case a.kode
when '05' then
a.jumlah
else
null
end) as LIMARIBU,
(case a.kode
when '06' then
a.jumlah
else
null
end) as DUARIBU,
(case a.kode
when '07' then
a.jumlah
else
null
end) as SATURIBU,
b.jumlahfisik,
b.selisih,
b.bon,
b.uangrusak,
b.materai,
b.lain,
b.jumlahtotal
from kasir_opname_detail a
left outer join kasir_opname_header b
on a.tanggal = b.tanggal
where a.ptlokasi = '0';
This dbgrid will be a field where I can get values to save the data into database. (Not using apply updates, but using a button with insert code in it). However, the columns where I use the case code cannot be filled with anything.
Is there any idea how to put data to those columns? Or is there any other way to make it possible?

Oracle/SQL Query issue

I am sort of a newbie to oracle/sql. I am trying to pull from the same column different values and add them with some other information. The other information is not the issue it is trying to count and add here the problem comes in.
I am connecting to an oracle database. Here is what i have
SELECT
EV.PUBLIC_DESCRIPTION,
EV.EVENT_DATE,
ES.PRICE,
BT.BUYER_TYPE_CODE,
PCA.ADDR1,
PCA.ADDR2,
PCA.CITY,
PCA.POSTAL_CODE,
PCE.EMAIL,
PC.FORMATTED_NAME,
PCP.PHONE_NUMBER,
PCP.SECONDARY,
SUM(COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRADLT' THEN 1 ELSE 0 END) + COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRADTE' THEN 1 ELSE 0 END) + COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRSTND' THEN 1 ELSE 0 END) + COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GSTDTE' THEN 1 ELSE 0 END) + COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GROUDI' THEN 1 ELSE 0 END)) AS "Adults",
SUM(COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRCHILD' THEN 1 ELSE 0 END) + COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRCHTE' THEN 1 ELSE 0 END)) AS 'Paid Child',
SUM(COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRPCH' THEN 1 ELSE 0 END)) AS 'Free Child',
SUM(COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRCOMP' THEN 1 ELSE 0 END)) AS 'Comps'
FROM EVENT EV
INNER JOIN EVENT_SEAT ES ON EV.EVENT_ID = ES.EVENT_ID
INNER JOIN BUYER_TYPE BT ON ES.BUYER_TYPE_ID = BT.BUYER_TYPE_ID
INNER JOIN PATRON_ORDER PO ON ES.ORDER_ID = PO.ORDER_ID
INNER JOIN PATRON_ACCOUNT PA ON ES.ATTENDING_PATRON_ACCOUNT_ID = PA.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT PC ON PA.PATRON_ACCOUNT_ID = PC.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_ADDRESS PCA ON PC.PATRON_ACCOUNT_ID = PCA.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_EMAIL PCE ON PCA.PATRON_ACCOUNT_ID = PCE.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_PHONE PCP ON PCE.PATRON_ACCOUNT_ID = PCP.PATRON_ACCOUNT_ID
GROUP BY EV.PUBLIC_DESCRIPTION, EV.EVENT_DATE
ORDER BY ES.TRANSACTION_ID DESC, PCP.SECONDARY DESC, PCP.PHONE_NUMBER DESC, PC.FORMATTED_NAME DESC, PCE.EMAIL DESC, PCA.POSTAL_CODE DESC, PCA.CITY DESC, PCA.ADDR2 DESC, PCA.ADDR1 DESC, BT.BUYER_TYPE_CODE DESC, ES.PRICE DESC;
any help would be greatly appreciated
You need to decide which technique you want to use, currently you are using 2 techniques and they are colliding.
For this you must know: COUNT() will increment by one for every NON-NULL value
So, to use COUNT() with a case expression do this
COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRSTND' THEN 1 END)
or
COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRSTND' THEN 1 ELSE NULL END)
OR, don't use COUNT(), use SUM() instead
SUM(CASE WHEN BT.BUYER_TYPE_CODE = 'GRSTND' THEN 1 ELSE 0 END)
To add conditions together, I suggest you use the case expression better
Instead of something like this:
, COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRADLT' THEN 1 END)
+ COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRADTE' THEN 1 END)
+ COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRSTND' THEN 1 END)
+ COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GSTDTE' THEN 1 END)
+ COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GROUDI' THEN 1 END) AS "Adults"
Use this:
COUNT(CASE WHEN BT.BUYER_TYPE_CODE IN ('GRADLT','GRADTE','GRSTND','GSTDTE','GROUDI') THEN 1 ELSE NULL END)
There is also an issue with your GROUP BY, which MUST contain ALL non-aggregating columns. I think your query should look more like this:
SELECT
EV.PUBLIC_DESCRIPTION
, EV.EVENT_DATE
, ES.PRICE
/* , BT.BUYER_TYPE_CODE */
, PCA.ADDR1
, PCA.ADDR2
, PCA.CITY
, PCA.POSTAL_CODE
, PCE.EMAIL
, PC.FORMATTED_NAME
, PCP.PHONE_NUMBER
, PCP.SECONDARY
, COUNT(CASE WHEN BT.BUYER_TYPE_CODE IN ('GRADLT', 'GRADTE', 'GRSTND', 'GSTDTE', 'GROUDI') THEN 1 ELSE NULL END) AS "Adults"
, COUNT(CASE WHEN BT.BUYER_TYPE_CODE IN ('GRCHILD', 'GRCHTE', 'GRPCH', 'GRCOMP') THEN 1 ELSE NULL END) AS "Free Child"
, COUNT(CASE WHEN BT.BUYER_TYPE_CODE = 'GRCOMP' THEN 1 ELSE NULL END) AS "Comps"
FROM EVENT EV
INNER JOIN EVENT_SEAT ES
ON EV.EVENT_ID = ES.EVENT_ID
INNER JOIN BUYER_TYPE BT
ON ES.BUYER_TYPE_ID = BT.BUYER_TYPE_ID
INNER JOIN PATRON_ORDER PO
ON ES.ORDER_ID = PO.ORDER_ID
INNER JOIN PATRON_ACCOUNT PA
ON ES.ATTENDING_PATRON_ACCOUNT_ID = PA.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT PC
ON PA.PATRON_ACCOUNT_ID = PC.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_ADDRESS PCA
ON PC.PATRON_ACCOUNT_ID = PCA.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_EMAIL PCE
ON PCA.PATRON_ACCOUNT_ID = PCE.PATRON_ACCOUNT_ID
INNER JOIN PATRON_CONTACT_PHONE PCP
ON PCE.PATRON_ACCOUNT_ID = PCP.PATRON_ACCOUNT_ID
GROUP BY
EV.PUBLIC_DESCRIPTION
, EV.EVENT_DATE
, ES.PRICE
/* , BT.BUYER_TYPE_CODE */
, PCA.ADDR1
, PCA.ADDR2
, PCA.CITY
, PCA.POSTAL_CODE
, PCE.EMAIL
, PC.FORMATTED_NAME
, PCP.PHONE_NUMBER
, PCP.SECONDARY
/* check all these columns exist in the select clause
ORDER BY
ES.TRANSACTION_ID DESC
, PCP.SECONDARY DESC
, PCP.PHONE_NUMBER DESC
, PC.FORMATTED_NAME DESC
, PCE.EMAIL DESC
, PCA.POSTAL_CODE DESC
, PCA.CITY DESC
, PCA.ADDR2 DESC
, PCA.ADDR1 DESC
, BT.BUYER_TYPE_CODE DESC
, ES.PRICE DESC
*/
When you come the the final clause: ORDER BY you can ONLY reference columns that exist in the select clause. This example would FAIL
select column1 from table1 group by column1 order by fred
but this would work:
select column1 from table1 group by column1 order by column1
Your column aliases 'Paid Child', 'Free Child', 'Comps' should not be wrapped in single quotes. You should be using double quotes like you are already for "Adults".
So they should instead be:
"Paid Child"
"Free Child"
"Comps"
Or better yet, consider naming your aliases without any spaces, so you don't have to worry about wrapping the aliases in anything, like this:
paid_child
free_child
comps
Documentation on Database Object Names and Qualifiers:
Database Object Naming Rules
Every database object has a name. In a SQL statement, you represent the name of an object with a quoted identifier or a nonquoted identifier.
A quoted identifier begins and ends with double quotation marks ("). If you name a schema object using a quoted identifier, then you must use the double quotation marks whenever you refer to that object.
A nonquoted identifier is not surrounded by any punctuation.
...
Although column aliases, table aliases, usernames, and passwords are not objects or parts of objects, they must also follow these naming rules unless otherwise specified in the rules themselves.

Resources