oracle: pivot on dynamic dates - sql

I have this query:
select pvt1.*
from
(
select
TO_CHAR(DateAppointment, 'yyyy-mm-dd') as currentDay,
count(*) myCounter
[...]
from (
select
[...]
from myTable
) a
group by [...]
order by DateAppointment
) source1
PIVOT
(
max(myCounter)
--FOR currentDay IN ('2012-08-20', '2012-08-21', '2012-08-27', '2012-09-03')
FOR currentDay IN (
SELECT LISTAGG(datevalue, ', ')
WITHIN GROUP (ORDER BY datevalue)
FROM DATESLIST
)
) pvt1;
This subquery just get the list of my dates from another table (DATESLIST), but when i run the first query, Oracle returns an error.
SELECT LISTAGG(datevalue, ', ')
WITHIN GROUP (ORDER BY datevalue)
FROM DATESLIST
But when i use instead the following code, i get the correct results:
FOR currentDay IN ('2012-08-20', '2012-08-21', '2012-08-27', '2012-09-03')
Any ideas?
Thanks in advance.

Related

Get range of dates from dates record in MS SQL

I have dates record
with DateTable (dateItem) as
(
select '2022-07-03' union all
select '2022-07-05' union all
select '2022-07-04' union all
select '2022-07-09' union all
select '2022-07-12' union all
select '2022-07-13' union all
select '2022-07-18'
)
select dateItem
from DateTable
order by 1 asc
I want to get ranges of dates between this record like this
with DateTableRange (dateItemStart, dateItemend) as
(
select '2022-07-03','2022-07-05' union all
select '2022-07-09','2022-07-09' union all
select '2022-07-12','2022-07-13' union all
select '2022-07-18','2022-07-18'
)
select dateItemStart, dateItemend
from DateTableRange
I am able to do it in SQL with looping using while or looping by getting first one and check the next dates and if they are 1 plus then I add it in enddate and do the same in loop
But I don't know what the best or optimized way is, as there were lots of looping and temp tables involve
Edited :
as in data we have 3,4,5 and 6,7,8 is missing so range is 3-5
9 exist and 10 is missing so range is 9-9
so ranges is purely depend on the consecutive data in datetable
Any suggestion will be appreciated
With some additional clarity this requires a gaps-and-islands approach to first identify adjacent rows as groups, from which you can then use a window to identify the first and last value of each group.
I'm sure this could be refined further but should give your desired results:
with DateTable (dateItem) as
(
select '2022-07-03' union all
select '2022-07-05' union all
select '2022-07-04' union all
select '2022-07-09' union all
select '2022-07-12' union all
select '2022-07-13' union all
select '2022-07-18'
), valid as (
select *,
case when exists (
select * from DateTable d2 where Abs(DateDiff(day, d.dateitem, d2.dateitem)) = 1
) then 1 else 0 end v
from DateTable d
), grp as (
select *,
Row_Number() over(order by dateitem) - Row_Number()
over (partition by v order by dateitem) g
from Valid v
)
select distinct
Iif(v = 0, dateitem, First_Value(dateitem) over(partition by g order by dateitem)) DateItemStart,
Iif(v = 0, dateitem, First_Value(dateitem) over(partition by g order by dateitem desc)) DateItemEnd
from grp
order by dateItemStart;
See Demo Fiddle
After clarification, this is definitely a 'gaps and islands' problem.
The solution can be like this
WITH DateTable(dateItem) AS
(
SELECT * FROM (
VALUES
('2022-07-03'),
('2022-07-05'),
('2022-07-04'),
('2022-07-09'),
('2022-07-12'),
('2022-07-13'),
('2022-07-18')
) t(v)
)
SELECT
MIN(dateItem) AS range_from,
MAX(dateItem) AS range_to
FROM (
SELECT
*,
SUM(CASE WHEN DATEADD(day, 1, prev_dateItem) >= dateItem THEN 0 ELSE 1 END) OVER (ORDER BY rn) AS range_id
FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY dateItem) AS rn,
CAST(dateItem AS date) AS dateItem,
CAST(LAG(dateItem) OVER (ORDER BY dateItem) AS date) AS prev_dateItem
FROM DateTable
) groups
) islands
GROUP BY range_id
You can check a working demo

Parametrizing dates in SQL IN clause - using cell magic in jupyter notebook

Using MSSQL db as the backend, I have a cell in my notebook with this sql which works fine.
%%sql
select * from (
select count(*) as CNT, COL1,CONVERT(VARCHAR,CAST(CREATED_DATE AS date)) as dt from TABLE1
group by COL1,CONVERT(VARCHAR,CAST(CREATED_DATE AS date))
)t
PIVOT
(
sum(CNT) for [dt] in ([2022-07-25],[2022-07-26])
) AS PivotTable
I am trying to parameterize the [IN] clause in the pivot.
Tried a few things, but without much success
import pandas as pd
from datetime import datetime
rng = pd.date_range(end = datetime.today(), periods = 5).strftime('%Y-%m-%d').tolist()
#rng = format(','.join('[{}]'.format(i) for i in rng))
#rng = pd.date_range(end = datetime.today().date(), periods = 5)
print (rng)
['2022-07-26', '2022-07-27', '2022-07-28', '2022-07-29', '2022-07-30']
select * from (
select count(*) as CNT, COL1,CONVERT(VARCHAR,CAST(CREATED_DATE AS date)) as dt from TABLE1
group by COL1,CONVERT(VARCHAR,CAST(CREATED_DATE AS date))
)t
PIVOT
(
sum(CNT) for [dt] in (:rng)
) AS PivotTable
* mssql+pymssql://---
(pymssql._pymssql.ProgrammingError) (102, b"Incorrect syntax near '('.DB-Lib error message 20018, severity 15:\nGeneral SQL Server error: Check messages from the SQL Server\n")
[SQL: select * from (
select count(*) as CNT, COL1,CONVERT(VARCHAR,CAST(CREATED_DATE AS date)) as dt from TABLE1
group by COL1,CONVERT(VARCHAR,CAST(CREATED_DATE AS date))
)t
PIVOT
(
sum(CNT) for [dt] in (%(rng)s)
) AS PivotTable]
[parameters: {'rng': ['2022-07-26', '2022-07-27', '2022-07-28', '2022-07-29', '2022-07-30']}]
(Background on this error at: https://sqlalche.me/e/14/f405)
any ideas on how I can achieve this. I will try creating the entire query dynamically but it will be much better if I can pass the dates alone into the query.
thanks for your time.

Avoid division by zero: 1 / 0 error in WITH clause

I am using the following in a WITH clause to create a FULL JOIN in Big Query:
WITH
a AS(
SELECT
date AS Date,
SUM(Val1 / (1 - (Val2 + Val3))) AS Calc1,
FROM `project.dataset.table1`
GROUP BY Date
),
b as (SELECT
date AS Date,
FROM `project.dataset.table2`
GROUP BY Date
)
SELECT a.Date, SUM(Calc1)
FULL JOIN a on b.Date = a.Date
GROUP BY b.Date
Calc1 is creating a 'division by zero: 1 / 0' error, and I can't seem to work out how to restructure this so it doesn't occur. The query works fine outside of the WITH clause, as I can simply not include the GROUP BY so have no need to SUM Calc1?
Below is for BigQuery Standard SQL
Use
SUM(SAFE_DIVIDE(Val1, 1 - (Val2 + Val3))) AS Calc1
instead of
SUM(Val1 / (1 - (Val2 + Val3))) AS Calc1
Use NULLIF :
WITH
a AS(
SELECT
date AS Date,
SUM(Val1 / NULLIF((1 - (Val2 + Val3)),0)) AS Calc1,
FROM `project.dataset.table1`
GROUP BY Date, SUM(Calc1)
)
Have you analysed your data within "table1" to ensure that Val1, Val2 & Val3 are consistently populated, or do you have NULL values?
This could be the issue with your subtraction from 1.
WITH
a AS(
SELECT
date AS Date,
SUM(Val1 / (1 - (isnull(Val2,0.00) + isnull(Val3,0.00)))) AS Calc1
FROM `project.dataset.table1`
GROUP BY Date
),
b as (
SELECT
date AS Date,
FROM `project.dataset.table2`
GROUP BY Date
)
SELECT a.Date, SUM(a.Calc1)
FULL JOIN a on b.Date = a.Date`enter code here`
GROUP BY b.Date

PIVOT table returning all null data

Consider this query:
SELECT *
FROM (
SELECT
userid,
FORMAT(datecreated, 'yyyy-MM') AS purchasemonth,
COALESCE(amount + tip, 0) AS amt
FROM invoice
) AS SourceTable
which produces output like this:
And this pivot query in which I am trying to sum over each month:
SELECT
userid,
COALESCE([2016-08-01], 0) AS [2016-08-01],
COALESCE([2016-09-01], 0) AS [2016-09-01]
FROM (
SELECT
userid,
FORMAT(datecreated, 'yyyy-MM') AS purchasemonth,
COALESCE(amount + tip, 0) AS amt
FROM invoice
) AS SourceTable
PIVOT
(
SUM(amt)
FOR purchasemonth IN ([2016-08-01], [2016-09-01])
) AS PivotTable
which produces output like this:
There is no NULL data at all in the original query's output. The PIVOT query's output is nothing but null data (coalesced to 0). But I can't figure out why the PIVOT is not summing the data as I expected. I'm expecting there to be no NULL data in the PIVOT output either.
How can I fix the query to behave as expected?
PurchaseMonth in your Derived Table is a String without DAYs in it and you are comparing it to a Date with days in it for the values/column names:
So the main issue is this line:
FOR purchasemonth IN ([2016-08-01], [2016-09-01])
TO
FOR purchasemonth IN ([2016-08], [2016-09])
Once you change that you would need to change the COALESCE() statements too
and you should get what you want.
SELECT
userid,
COALESCE([2016-08], 0) AS [2016-08-01],
COALESCE([2016-09], 0) AS [2016-09-01]
FROM (
SELECT
userid,
FORMAT(datecreated, 'yyyy-MM') AS purchasemonth,
COALESCE(amount + tip, 0) AS amt
FROM invoice
) AS SourceTable
PIVOT
(
SUM(amt)
FOR purchasemonth IN ([2016-08], [2016-09])
) AS PivotTable
If you want the 01 to remain for days then simply change up the Derived Table definition to be a date or include the day in the format
So if you want to go this route change this line:
FORMAT(datecreated, 'yyyy-MM') AS purchasemonth,
To
DATEADD(day,1-DAY(datecreated),datecreated) AS purchasemonth,
you could also use this
FORMAT(datecreated, 'yyyy-MM-dd') AS purchasemonth,
But FORMAT has performance impacts that you have no reason to introduce if you don't need to.
SELECT
userid,
COALESCE([2016-08-01], 0) AS [2016-08-01],
COALESCE([2016-09-01], 0) AS [2016-09-01]
FROM (
SELECT
userid,
DATEADD(day,1-DAY(datecreated),datecreated) AS purchasemonth,
COALESCE(amount + tip, 0) AS amt
FROM invoice
) AS SourceTable
PIVOT
(
SUM(amt)
FOR purchasemonth IN ([2016-08-01], [2016-09-01])
) AS PivotTable

Oracle sub error on query

Following code I added to the SQL Server query and now have to do the same in Oracle. I need to do grouping in the view rather than in the C#. I get this error message:
ORA-01747 Invalid user.table.column or column specification.
How must I code this to work in Oracle?
SELECT CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS, COUNT(*) AS [COUNT]
FROM CTE
GROUP BY CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS;
at the beginning of query I have this full code here:
CREATE OR REPLACE VIEW DBD_V_CDL_CHANGES AS
WITH CTE AS
(
SELECT TR.FACILITY_KEY
, MV.VALUE_CODE
, CAST(COUNT(*) AS NUMERIC(9, 0)) COUNT
FROM OPTC.THS_T_TRANSACTIONS1 TR
JOIN OPTC.THS_M_MENU2 M
ON M.MENU_ID = TR.MENU_ID
JOIN OPTC.THS_M_VALUES MV
ON MV.MENU_ID = TR.MENU_ID_VALUE
JOIN OPTC.THS_M_VALUES MV2
ON MV2.MENU_ID = TR.PREVIOUS_MENU_ID_VALUE
JOIN OGEN.GEN_M_PATIENT_MAST PM
ON PM.PAT_NUMBER = TR.PAT_NUMBER
WHERE TR.TR_DATETIME BETWEEN TRUNC(SYSDATE)
AND TRUNC(SYSDATE) + 86399 / 86400
AND TR.EDIT_NO < 0
AND MV.VALUE_TYPE IS NULL
AND MV2.VALUE_TYPE IS NULL
AND MV.VALUE_CODE >= 0
AND MV2.VALUE_CODE >= 0
AND M.SUB_SYS_EXT = 'G1'
AND ABS(MV.VALUE_CODE - MV2.VALUE_CODE) > 1
AND (PM.DISCHARGE_DATE IS NULL OR PM.DISCHARGE_DATE < SYSDATE)
GROUP BY TR.FACILITY_KEY, MV.VALUE_CODE)
SELECT CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS, COUNT(*) AS [COUNT] FROM CTE
GROUP BY CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS;
I see a few things wrong with your code.
First, you are selecting the following three columns FACILITY_KEY, VALUE_CODE and the count in the CTE:
SELECT TR.FACILITY_KEY ,
MV.VALUE_CODE ,
COUNT(*) as Count -- note there is no need to CAST(COUNT(*) AS NUMERIC(9, 0)) this
FROM OPTC.THS_T_TRANSACTIONS1 TR
But then when you select from the CTE you are selecting columns that you are not returning in the CTE:
with cte as
(
-- your query here does not return DATE or PATIENT_STATUS
)
SELECT CTE.FACILITY_KEY,
CTE.DATE,
CTE.PATIENT_STATUS,
COUNT(*) AS COUNT
FROM CTE
GROUP BY CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS;
Where do PATIENT_STATUS and Date come from since you are not including them in your CTE? So these do not exist when you are trying to select them.
I replicated your error by including columns in the list that were not select in the CTE query.
The second issue is the CTE.DATE column. DATE is a reserved word, place that is double quotes CTE."DATE"
...AS [COUNT], ...AS NUMERIC(9, 0)) is not Oracle syntax and will never work. Simply remove [ ] and use NUMBER instead of NUMERIC. There is no need to CAST Count(). The Count() function will always return number, e.g. 0-zero or some number.
This is valid syntax in Oracle:
SELECT deptno, count(*) total_count_by_dept -- no need to cast or AS --
FROM scott.emp
GROUP BY deptno
/
Try not to use reserved words as COUNT for aliases:
SELECT CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS, COUNT(*) AS total_cnt -- 'AS' is for clarity only, not required
FROM CTE
GROUP BY CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS
/