Passing Bind Variable in case statements in Oracle query - oracle-sqldeveloper

I want to pass Bind Variable (a Date) in a Case statement in order to achieve the following:
When User inputs a date and if that Date is falling on Monday, then the Case statement should fetch the value of Friday (meaning it should bypass the weekends and look for the values of a previous working day)
I tried to use the following query which works well when I use 'sysdate'
Select * from table_name
Where tradedate = trunc (sysdate - case to_char(sysdate, 'Dy')
when 'Mon' then 3 else 1 end);
but when I replace 'sysdate' with a Bind Variables, it gives me an error like:
tradedate = trunc (:sysdate1 - case to_char(:sysdate2, 'Dy')
when 'Mon' then 3 else 1 end);
ORA-00932: inconsistent datatypes: expected DATE got NUMBER
00932. 00000 - "inconsistent datatypes: expected %s got %s"
Can't we use Bind Variables in Case statement in oracle queries? If so, Can someone please give me any alternate solution to my problem stated above?
Any help would be really appreciated.
Below is the complete code:
select * from (
SELECT
S."TRADEDATE",S."ACCOUNT_NAME",S."BOOKING_AMOUNT",S."ACCOUNT_NUMBER",(CASE WHEN BOOKING_AMOUNT <0 THEN S."CREDIT" ELSE S."DEBIT" END) AS "DEBIT" , (CASE WHEN BOOKING_AMOUNT <0 THEN S."DEBIT" ELSE S."CREDIT" END) AS "CREDIT",
U.VALUE_DT , U.AC_NO , NVL(U.BOOKED_AMOUNT ,0) BOOKED_AMOUNT
FROM
SXB S
FULL OUTER JOIN UBS U ON
S.ACCOUNT_NUMBER = U.AC_NO
AND
S.TRADEDATE = U.VALUE_DT
UNION ALL
SELECT
BOOKING_DATE TRADEDATE,
'SAXO RECON' ACCOUNT_NAME,
SUM((Case when DR_CR_INDICATOR = 'D' then AMOUNT*-1 when DR_CR_INDICATOR = 'C' then AMOUNT end)) BOOKING_AMOUNT,
EXTERNAL_ACCOUNT ACCOUNT_NUMBER,
'Matched - ' ||A.MATCH_INDICATOR AS DEBIT,
NULL AS CREDIT,
VALUE_DATE VALUE_DT,
NULL AS AC_NO,
0 AS BOOKED_AMOUNT
FROM
FCUBS.RETB_EXTERNAL_ENTRY A
WHERE A.EXTERNAL_ENTITY = 'SAXODKKKXXX'
AND A.EXTERNAL_ACCOUNT = '78600/COMMEUR'
group by
BOOKING_DATE ,
EXTERNAL_ACCOUNT ,
VALUE_DATE,
MATCH_INDICATOR
order by tradedate, account_name)
where tradedate = trunc (:sysdate1 - case to_char(:sysdate2, 'Dy') when 'Mon' then 3 else 1 end);

SYSDATE is a date datatype so oracle will always treat it as a DATE datatype. For a bind variable I'd do an explicit conversion using TO_DATE(:bind_var, 'FORMAT_MASK'). For example:
select
case TO_CHAR(TO_DATE(:sysdate2,'DD-MON-YYYY'), 'Dy') when 'Mon'
then 3
else 1
end from dual

Related

In PostgreSQL, how can I optimize a query with which I obtain the differences between the current column and the immediately previous one?

I have this audit table
User
date
text
text 2
u1
2023-01-01
hi
yes
u1
2022-12-20
hi
no
u1
2022-12-01
hello
maybe
And I need as a result, something like this:
User
date
text
text 2
u1
2023-01-01
null
x
u1
2022-12-20
x
x
u1
2022-12-01
null
null
So I can know which column changed from the last time.
Something like this is working, but I think may be a way to optimize it? or at least generate a "more easy to look" query? (i need the information for almost 20 columns, not only 3)
SELECT
ta.audit_date,
ta.audit_user,
CASE
WHEN ta.audit_operation = 'I' THEN 'Insert'
WHEN ta.audit_operation = 'U' THEN 'Update'
END AS action,
CASE WHEN ta.column1 <> (SELECT column1
FROM audit_table ta1
WHERE ta1.id = 9207 AND ta1.audit_date < ta.audit_date
ORDER BY ta1.audit_date DESC
LIMIT 1)
THEN 'X' ELSE null END column1,
CASE WHEN ta.column2 <> (SELECT column2
FROM audit_table ta1
WHERE ta1.id = 9207 AND ta1.audit_date < ta.audit_date
ORDER BY ta1.audit_date DESC
LIMIT 1)
THEN 'X' ELSE null END column2,
CASE WHEN ta.column3 <> (SELECT column3
FROM audit_table ta1
WHERE ta1.id = 9207 AND ta1.audit_date < ta.audit_date
ORDER BY ta1.audit_date DESC
LIMIT 1)
THEN 'X' ELSE null END column3
FROM
audit_table ta
WHERE
ta.id = 9207
ORDER BY
audit_date DESC
Thank you!
I think you can just use the LAG() analytic function here. If I understand correctly:
SELECT *, CASE WHEN text != LAG(text) OVER (ORDER BY date) THEN 'x' END AS text_label,
CASE WHEN text2 != LAG(text) OVER (ORDER BY date) THEN 'x' END AS text2_label
FROM yourTable
ORDER BY date;

Mssql err: Column 'dbo.History.Email' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause

when executing this MSSQL statement it forces me to add the CASE WHEN field History.Email in the group by field, however I need to group only on a date field.
Any idea how this can be solved? (sql 2008)
SELECT TOP (100)
convert(varchar, VisitDate, 111) as Date,
SUM(AmountLoaded) AS Loaded,
CASE when History.Email is null then
SUM(AmountCash)
else SUM(AmountToPay)
end AS Total,
CASE when History.Email is null then
0
else SUM(AmountToPay)
end AS App,
SUM(AmountToPay) AS Consumed
FROM dbo.History
GROUP BY convert(varchar, VisitDate, 111)
order by 1 desc
When performing conditional aggregation you need to put the entire CASE expression in an aggregate, not just the expression(s) you want to aggregate:
SELECT TOP (100)
CONVERT(varchar(10), H.VisitDate, 111) AS [Date], --Always specify a length/precision/scale for your data types
SUM(H.AmountLoaded) AS Loaded,
SUM(CASE WHEN H.Email IS NULL THEN H.AmountCash ELSE H.AmountToPay END) AS Total,
SUM(CASE WHEN H..Email IS NULL THEN 0 ELSE H.AmountToPay END) AS App,
SUM(H.AmountToPay) AS Consumed
FROM dbo.History H
GROUP BY CONVERT(varchar(10), H.VisitDate, 111) --Always specify a length/precision/scale for your data types
ORDER BY [Date] DESC; --Be specific, don't use ordinal positions
Note, as well, I suggest not converting your column VisitDate to a varchar in your query. If you want to display the data in a specific format then that's something for your presentation layer to do, not the RDBMS. Fortunately, as style 111 is yyyy/MM/dd then the order of the data isn't affected. If, however, you want to simply truncate the time portion of your value, convert it to a date so that the value continues to remain strongly typed.
By definition any nonaggregate fields should be in the group by clause. You can write that as:
SELECT TOP (100)
convert(varchar, VisitDate, 111) as Date,
SUM(AmountLoaded) AS Loaded,
SUM(CASE when History.Email is null
then AmountCash
else AmountToPay end) AS Total,
SUM(CASE when History.Email is null
then 0
else AmountToPay
end) AS App,
SUM(AmountToPay) AS Consumed
FROM dbo.History
GROUP BY convert(varchar, VisitDate, 111)
order by 1 desc

Combine DATETIME column with CHAR field to create DATETIME2 column

I am trying to import data from a vendor's database and combine two columns into one for our database. They have two columns are CITATION_DATE which is a datetime data type and CITATION_TIME which is a char(8) data type.
I would like to combine these two columns into one column issueDate which is of datetime2(7) data type.
I tried using Aaron's logic found here but I have not been successful in getting the query to execute. My suspicion is that I need more characters in the CITATION_TIME to form a valid time stamp but I am not sure.
Is there a way to combine these two fields into a single column - that follows the datetime2 format?
My attempt was to try and clean up bad values like empty strings or non-numeric characters:
;WITH issueDate AS
(
SELECT
TRY_CAST(vt.CITATION_DATE AS DATE) AS CITATION_DATE ,
CASE WHEN RTRIM ( LTRIM ( vt.CITATION_TIME )) = '' THEN '0000'
WHEN RTRIM ( LTRIM ( vt.CITATION_TIME )) = '000' THEN '0000'
WHEN TRY_CAST(vt.CITATION_TIME AS INT) IS NULL THEN '0000'
ELSE vt.CITATION_TIME
END AS CITATION_TIME
FROM
Oklahoma_PVD_WildlifeLaw.dbo.VIOLATOR_TICKETS AS vt
--ORDER BY CITATION_TIME;
)
SELECT
CONVERT(DATETIME, CONVERT(CHAR(8), id.CITATION_DATE, 112) + ' ' +
CONVERT(CHAR(8), id.CITATION_TIME, 108))
FROM
issueDate AS id;
But I am getting the following error:
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value
It does look like I do not have enough characters for the TIME portion - as this return values in a DATETIME format, but I lose all of my hours and minutes this way:
;WITH issueDate AS
(
SELECT
TRY_CAST(vt.CITATION_DATE AS DATE) AS CITATION_DATE,
CASE
WHEN RTRIM(LTRIM(vt.CITATION_TIME)) = '' THEN '00:00:00.0000000'
WHEN RTRIM(LTRIM(vt.CITATION_TIME)) = '000' THEN '00:00:00.0000000'
WHEN TRY_CAST(vt.CITATION_TIME AS INT) IS NULL THEN '00:00:00.0000000'
--ELSE vt.CITATION_TIME
END AS CITATION_TIME
FROM
Oklahoma_PVD_WildlifeLaw.dbo.VIOLATOR_TICKETS AS vt
--ORDER BY CITATION_TIME;
)
SELECT
CONVERT(DATETIME, CONVERT(CHAR(8), id.CITATION_DATE, 112) + ' ' +
CONVERT(CHAR(8), id.CITATION_TIME, 108)) AS [DateTime]
FROM
issueDate AS id
WHERE
CONVERT(DATETIME, CONVERT(CHAR(8), id.CITATION_DATE, 112) + ' ' +
CONVERT(CHAR(8), id.CITATION_TIME, 108)) IS NOT NULL;
Did you tried to calculate number of minutes from the time part and add them to the date using DATEADD?
select DATEADD(minute, cast(CITATION_TIME as int) % 100 + 60 * (cast(CITATION_TIME as int) / 100), CITATION_DATE)
FROM Oklahoma_PVD_WildlifeLaw.dbo.VIOLATOR_TICKETS AS vt
This code assumes all dates has zero time part (midnight) and CITATION_TIME are all digits. The code above tries to handle these by casting the datetime to date (to get rid of the time part) and using try_cast to handle non-numeric times. If you need, you can use the same code:
select DATEADD(minute, t.IntTime % 100 + 60 * (t.IntTime / 100), cast(CITATION_DATE as date))
FROM Oklahoma_PVD_WildlifeLaw.dbo.VIOLATOR_TICKETS AS vt
cross apply (select isnull(try_cast(CITATION_TIME as int), 0) as IntTime ) t
If there is invalid "times" in your data, you must decide how to handle them. Try to find these records by searching for values, where last 2 digits are >= 60 for example:
select * from Oklahoma_PVD_WildlifeLaw.dbo.VIOLATOR_TICKETS
where try_cast(right(CITATION_TIME, 2) as int) >= 60
or (len(CITATION_TIME) = 4 and try_cast(left(CITATION_TIME, 2) as int) >= 24)
or len(CITATION_TIME) > 4

SQL Select where exclusive Or /Case

I need a query based on an exclusive Or statement for this I try using the case but I won't get a result in case of Null...
...
and b.[U_Periode] = CASE
when (b.U_Periode= #period) then #period
when (b.U_Periode is NULL ) then null
end
...
The Case that won't be catched is... if B.U_Status is null and b.U_Periode is null.
If the var Periode match the Value and the U_Status = 0 or 1
the only way getting this working for me was this:
...
and
ISNULL( b.[U_Status],'0') = CASE
when (b.U_Status= '1') then '1'
when (isnull( b.U_Status,'0')= '0') then '0'
end
and
ISNULL (b.[U_Periode],'01.01.1901') = CASE
when (b.U_Periode= #period) then #period
when (ISNULL (b.U_Periode,'01.01.1901') = '01.01.1901' ) then '01.01.1901'
end
are there any other better solutions for this?
Best regards
Oliver
Okay... here is my Problem
Table1
InsID ContractID
1 1
2 1
Table2
ID insid Period Status Count
1 1 null null 100
2 1 30.09.2015 1 500
3 2 null null 100
4 2 30.09.2015 1 500
Case '31.08.2015'
in total Value should be 200
in case of '30.09.2015'
the Value should be 1.000
XOR /OR will do the same in this case.
Value case '31.08.2015' = 200
value Case ' 30.09.2015 = 2200
So this is somesing like a subquery
left join (
[dbo].[Table2]b
inner join [dbo].[Table 3]K on k.DocEntry = b.DocEntry and CAST( k.U_CSetID as int) >0
)
on b.[U_InsID] in(select... but here I should have an if statement...
If there are results matching the Date join this
If not than join the result NULL is matching to periode...
Okay.. here is the complete query
the Table2 with the Date of 31.08.2015 should have one Record that include
B_STATUS = Null and B.Preiode = null and there is no available record with the U_Periode '31.08.2015' and a Staus ...
with a date of 30.09.2015
there is a Record matching U_Period = '30.09.2015' in this case the Record with U_Period=null should not effect the result...
Declare #period as varchar(20)= '31-08-2015 00:00:00'
declare #Customer as Varchar(15)='12345'
declare #Contract as varchar(30) = '123'
declare #test as varchar(1) = null
select SUM(cast(K.U_Count as decimal))as counter, K.U_CounterTyp
from [dbo].[Table1] a
left join (
[dbo].[Table2]b
inner join [dbo].[Table 3]K on k.DocEntry = b.DocEntry and CAST( k.U_CSetID as int) >0
)
on b.[U_InsID]=a.[insID] and b.[U_ObjectType]in ('5','1') and
ISNULL( b.[U_Status],'0') = CASE
when (b.U_Status= '1') then '1'
when (isnull( b.U_Status,'0')= '0') then '0'
end
and
ISNULL (b.[U_Periode],'01.01.1901') = CASE
when (b.U_Periode= #period) then #period
when (ISNULL (b.U_Periode,'01.01.1901') = '01.01.1901' ) then '01.01.1901'
end
where a.[customer] =#Customer and a.[Status]='A' and a.[U_ContrCount]='1'
and a.[manufSN] in(
select c.[ManufSN] from [dbo].[Table4] c
inner join [dbo].[OCTR]d on d.[ContractID] = c.[ContractID]
where c.[ManufSN]=a.[manufSN]
and d.[CstmrCode] = a.[customer] and d.[ContractID]=#Contract
)
group by K.U_CounterTyp
you must use the "^" operand, this is XOR operand in TSQL
expression ^ expression
the expression must return or 0 or 1...
stupid example:
WHERE (name like "stackoverflow") ^ (age > 10)
font: https://msdn.microsoft.com/en-us/library/ms190277(v=sql.105).aspx
Update for your check
WHERE (CONVERT(VARCHAR(10), a.[U_Periode], 104) = '30.08.2015') != (a.U_Periode IS NULL)
Olay here is my funktion that chek either a Date Result is there or not.
The only thing is, that the performance is not the best if I run hundreths of sentences ... foreach record (up to app. 1000) I need to query each subtable... and there are many many records in...
Anyway this is the function
CREATE FUNCTION fn_GetInsCounters(#InsId as Varchar(30), #Date as datetime)
returns datetime
as
begin
declare #count as int = 0
declare #Retruns as Datetime
set #count =
(
select count( b.Docentry)
from [dbo].[Table2]b
where b.U_Insid =#InsID
and b.U_Status <> '2'
and b.[U_ObjectType]in ('5','1')
and b.U_Periode=#Date
)
if(#count>0)
begin
set #Retruns = #date
end
else
begin
set #Retruns = '01.01.1901'
end
return #Retruns
end
if anyone gets a better idea???
Best regards
Oliver

Two questions about my SQL script. How to add subtotal/total lines, and a sorting issue

I have some T-SQL that generates a nice report giving a summary some stuff by month.
I have 2 questions, is there a way to get the to sort the months by calendar order, not by alpha? And, what i would like to do is add a total line for each year, and a total line for the whole report?
SELECT
CASE WHEN tmpActivity.Year IS NULL THEN
CASE WHEN tmpCreated.Year IS NULL THEN
CASE WHEN tmpContactsCreated.Year IS NULL THEN
null
ELSE tmpContactsCreated.Year END
ELSE tmpCreated.Year END
ELSE tmpActivity.Year END As Year,
CASE WHEN tmpActivity.Month IS NULL THEN
CASE WHEN tmpCreated.Month IS NULL THEN
CASE WHEN tmpContactsCreated.Month IS NULL THEN
null
ELSE DateName(month, DateAdd(month, tmpContactsCreated.Month - 1, '1900-01-01' )) END
ELSE DateName(month, DateAdd(month, tmpCreated.Month - 1, '1900-01-01' )) END
ELSE DateName(month, DateAdd(month, tmpActivity.Month - 1, '1900-01-01' )) END As Month,
CASE WHEN tmpActivity.ActiveAccounts IS NULL THEN 0 ELSE tmpActivity.ActiveAccounts END AS ActiveAccounts,
CASE WHEN tmpCreated.NewAccounts IS NULL THEN 0 ELSE tmpCreated.NewAccounts END AS NewAccounts,
CASE WHEN tmpContactsCreated.NewContacts IS NULL THEN 0 ELSE tmpContactsCreated.NewContacts END AS NewContacts
FROM
(
SELECT YEAR(LastLogon) As Year, MONTH(LastLogon) As Month, COUNT(*) As ActiveAccounts
FROM Users
WHERE LastLogon >= '1/1/1800'
GROUP BY YEAR(LastLogon), MONTH(LastLogon)
) as tmpActivity
FULL JOIN
(
SELECT YEAR(Created) As Year, MONTH(Created) As Month, COUNT(*) As NewAccounts
FROM Users
WHERE Created >= '1/1/1800'
GROUP BY YEAR(Created), MONTH(Created)
) as tmpCreated ON tmpCreated.Year = tmpActivity.Year AND tmpCreated.Month = tmpActivity.Month
FULL JOIN
(
SELECT YEAR(Created) As Year, MONTH(Created) As Month, COUNT(*) As NewContacts
FROM Contacts
WHERE Created >= '1/1/1800'
GROUP BY YEAR(Created), MONTH(Created)
) as tmpContactsCreated ON tmpContactsCreated.Year = tmpCreated.Year AND tmpContactsCreated.Month = tmpCreated.Month
Order By Year DESC, Month DESC
To order by the month use the following:
ORDER BY DATEPART(Month,Created) ASC
DatePart() returns an integer for the part specified 1 for January, 2 for Febuary etc.
You're SQL could be helped with the COALESCE() and ISNULL() functions. This is the same as your first select:
SELECT
COALESCE(tmpActivity.Year,tmpCreated.Year,tmpContactsCreated.Year) as Year,
COALESCE(tmpActivity.Month,tmpCreated.Month,tmpContactsCreated.Month) as Month,
ISNULL(tmpActivity.ActiveAccounts,0) AS ActiveAccounts,
ISNULL(tmpCreated.NewAccounts,0) AS NewAccounts,
ISNULL(tmpContactsCreated.NewContacts,0) AS NewContacts
I think there is a bug in your select, I believe your last line has to be this:
) as tmpContactsCreated ON (tmpContactsCreated.Year = tmpCreated.Year AND tmpContactsCreated.Month = tmpCreated.Month) OR
(tmpContactsCreated.Year = tmpActivity.Year AND tmpContactsCreated.Month = tmpActivity.Month)
But I would have to test this to be sure.
Adding in rollups is hard to do -- typically this is done externally to the SQL in the control or whatever displays the results. You could do something like this (contrived example):
SELECT 1 as reportOrder, date, amount, null as total
FROM invoices
UNION ALL
SELECT 2 , null, null, sum(amount)
FROM invoices
ORDER BY reportOrder, date
or you could not have the "extra" total column and put it in the amount column.