How to avoid Internal_Function by TO_CHAR IN Oracle - oracle

I'm using Oracle 12C and I have the following code:
SELECT *
FROM
(
SELECT
*
FROM
t.tablea
WHERE
name = 'FIS'
) A
LEFT JOIN (
SELECT
*
FROM
t.tableb
WHERE
enabled = 1
) B ON b.id = a.id
AND TO_CHAR(b.createdate, 'dd-mm-yy hh24:mi') = TO_CHAR(a.createdate, 'dd-mm-yy hh24:mi')
Both a and b createdate are timestamp datatype.
Optimizer return an internal_function at TO_CHAR(b.createdate, 'dd-mm-yy hh24:mi') = TO_CHAR(a.createdate, 'dd-mm-yy hh24:mi') in Execution Plan
If I compare like this: 'AND b.createdate = a.createdate', It will lost 1000 rows that look like this '11-JUN-18 04.48.34.269928000 PM'. And If I change 269928000 to 269000000 It will work
Now, I don't want to using to_char to avoid internal_function(must create Function-based-Index)
Anyone can help me?

If I compare like this: AND b.createdate = a.createdate, It will lost 1000 rows that look like this 11-JUN-18 04.48.34.269928000 PM. And If I change 269928000 to 269000000 It will work
Your values appear to have a fractional seconds component and would have the TIMESTAMP data type. If so, you can use TRUNC( timestamp_value, 'MI' ) to truncate to the nearest minute.
SELECT *
FROM t.tablea a
LEFT OUTER JOIN t.tableb b
ON ( a.createdate >= TRUNC( b.createdate, 'MI' )
AND a.createdate < TRUNC( b.createdate, 'MI' ) + INTERVAL '1' MINUTE
AND a.id = b.id
AND b.enabled = 1
)
WHERE a.name = 'FIS'
This will remove the need to apply a function to one of the two tables (a.createdate in this case but you could swap them).

I don't even see the need for the subqueries:
SELECT a.*, b.*
FROM t.tablea a
LEFT JOIN t.tableb b
ON a.id = b.id AND
TRUNC(b.createdate, 'MI') = TRUNC(a.createdate, 'MI') AND
b.enabled = 1
WHERE
a.name = 'FIS'

Related

Why can't a query using 2 CTEs be linked to another table [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have something similar to the code below. I'm new to Oracle. It works fine until I try to join it to "anothertable" then states it can't see cte2.a. I can't find anything that explains why it can't see cte2.a
I need the data created by cte2 to further my query. Should i just do that inside of cte2 instead?
WITH cte 1 as (......),
cte2 as (....join tablename on tablename.x = cte1.x)
select *
from cte2
join anothertable on anothertable.a = cte2.a
Here is the actual query. It states "MAX_HEM"."PATIENT_ID": invalid identifier
WITH HGB AS(
SELECT DISTINCT
PH.PATIENT_ID,
PH.HEMATOLOGY_CD, --EX HGB
PH.PATIENT_HEMATOLOGY_ID,
PH.HEMATOLOGY_RESULT,
PH.TRANSACTION_DATE,
PH.TRANSACTION_TIME,
(TO_CHAR(PH.TRANSACTION_DATE) || ' ' || (TO_CHAR(TO_DATE(LPAD(PH.TRANSACTION_TIME,4,'0'),'HH24MI'),'HH:MI AM'))) AS HGB_DATETIME,
TO_NUMBER(TRIM(LEADING 0 FROM(TO_CHAR(ORDER_PRODUCT_INVENTORY.ISSUE_DATETIME,'HH24MI')))) AS ISSUE_TIME,
ORDER_PRODUCT_INVENTORY.ISSUE_DATETIME || ' ' || (TO_CHAR(ORDER_PRODUCT_INVENTORY.ISSUE_DATETIME, 'HH:MI AM')) AS ISSUE_DTTM,
PRODUCT_INVENTORY.UNIT_NO,
CD_TRANSACTION.WTCODE
FROM
PRODUCT_INVENTORY_ACTIVITY PRODUCT_INVENTORY_ACTIVITY
JOIN CD_TRANSACTION CD_TRANSACTION ON PRODUCT_INVENTORY_ACTIVITY.TRANSACTION_CD = CD_TRANSACTION.TRANSACTION_CD
AND (CD_TRANSACTION.WTCODE='TA' OR CD_TRANSACTION.WTCODE='TX' OR CD_TRANSACTION.WTCODE='IE')
JOIN ORDER_PRODUCT_INVENTORY ORDER_PRODUCT_INVENTORY ON PRODUCT_INVENTORY_ACTIVITY.PRODINV_ID = ORDER_PRODUCT_INVENTORY.PRODINV_ID
JOIN ORDERS ORDERS ON ORDER_PRODUCT_INVENTORY.ORDER_ID = ORDERS.ORDER_ID
JOIN PATIENT_HEMATOLOGY PH ON PH.PATIENT_ID = ORDERS.PATIENT_ID
JOIN CD_ORDER_PRODUCT_INV_STAT CD_ORDER_PRODUCT_INV_STAT ON ORDER_PRODUCT_INVENTORY.ORDER_PRODUCT_INV_STAT_CD = CD_ORDER_PRODUCT_INV_STAT.ORDER_PRODUCT_INV_STAT_CD
JOIN PRODUCT_INVENTORY PRODUCT_INVENTORY ON ORDER_PRODUCT_INVENTORY.PRODINV_ID = PRODUCT_INVENTORY.PRODINV_ID
WHERE PH.HEMATOLOGY_CD = 'HGB'
AND PH.TRANSACTION_DATE <= TRUNC(ORDER_PRODUCT_INVENTORY.ISSUE_DATETIME)
AND PH.TRANSACTION_TIME <= TO_NUMBER(TRIM(LEADING 0 FROM(TO_CHAR(ORDER_PRODUCT_INVENTORY.ISSUE_DATETIME,'HH24MI'))))
AND CD_ORDER_PRODUCT_INV_STAT.WTCODE='TRANSFUSED'
AND (CD_TRANSACTION.WTCODE='TA' OR CD_TRANSACTION.WTCODE='TX' OR CD_TRANSACTION.WTCODE='IE')
AND ORDER_PRODUCT_INVENTORY.ISSUE_DATETIME IS NOT NULL
AND (ORDER_PRODUCT_INVENTORY.ISSUE_DATETIME>=TO_DATE ('01-09-2020 00:00:00', 'DD-MM-YYYY HH24:MI:SS')
AND ORDER_PRODUCT_INVENTORY.ISSUE_DATETIME<TO_DATE ('06-09-2020 00:00:00', 'DD-MM-YYYY HH24:MI:SS'))
ORDER BY PH.PATIENT_ID, UNIT_NO,TO_DATE(PH.TRANSACTION_DATE) DESC, PH.TRANSACTION_TIME DESC
),
MAX_HEM AS
(
SELECT
HGB.PATIENT_ID "PatID",
MAX(HGB.PATIENT_HEMATOLOGY_ID) "HemID",
MAX(HGB.TRANSACTION_DATE) "TransDate",
MAX(HGB.TRANSACTION_TIME) "TransTime",
MAX(HGB.HEMATOLOGY_RESULT) "HGB_Results",
MAX(HGB.UNIT_NO) "UnitNo"
FROM PATIENT_HEMATOLOGY PH1
LEFT JOIN HGB ON PH1.PATIENT_ID = HGB.PATIENT_ID
GROUP BY HGB.PATIENT_ID
ORDER BY HGB.PATIENT_ID
)
select
HGB.*,
MAXHEM.*
FROM MAX_HEM
LEFT JOIN ORDERS ORDERS ON ORDERS.PATIENT_ID = MAX_HEM.PATIENT_ID
I tried the below and its working for me. Just check is it something like this you are expecting?
WITH cte AS
( select 'a' as a from dual
union
select 'g' as a from dual
), cte2 AS
(select 'a' as b from dual
union
select 'c' as b from dual
)
SELECT *
FROM cte c inner JOIN cte2 c2 ON c.a = c2.b
Your query works:
WITH cte1 (x) as (
SELECT dummy FROM DUAL
),
cte2 (x, a) as (
SELECT tablename.x,
tablename.a
FROM cte1
join tablename
on tablename.x = cte1.x
)
select *
from cte2
join anothertable
on anothertable.a = cte2.a;
Which, for the sample data:
CREATE TABLE tablename ( x, a ) AS
SELECT dummy, ROWNUM FROM DUAL;
CREATE TABLE anothertable ( a ) AS
SELECT ROWNUM FROM DUAL;
Outputs:
X | A | A
:- | -: | -:
X | 1 | 1
db<>fiddle here

Exists query with not equal running extremly slow

I am trying to modify a query that someone else wrote which is taking a really long time to run. The problem has to do with the <> portion of the exists query. Any idea how this can be changed to run quicker?
SELECT m.level4 center, cc.description, m.employeename, m.empno,
TO_DATE (ct.tsdate, 'dd-mon-yyyy') tsdate, ct.starttime, ct.endtime,
ct.DURATION,
NVL (DECODE (ct.paycode, ' ', 'REG', ct.paycode), 'REG') paycode,
ct.titlecode, ct.costcenter, m.tsgroup
FROM clairvia_text ct, MASTER m, costcenteroutbound cc
WHERE ct.recordtype = '1'
AND ct.empno = m.empno
AND m.level4 = cc.center
AND EXISTS (
SELECT ct1.recordtype,ct1.empno,ct1.tsdate,ct1.processdate
FROM clairvia_text ct1
WHERE ct.recordtype = ct1.recordtype
AND ct.empno = ct1.empno
AND ct.tsdate = ct1.tsdate
AND ct.processdate = ct1.processdate
group by ct1.recordtype,ct1.empno,ct1.tsdate,ct1.processdate
having count(*) < 2)
Oracle can be finnicky with exists statements and subqueries. A couple of things to try:
Change the exists to an "in"
Change the exists to a group by statement with a "having count(1) > 1". This could even be changed into a join.
I'm assuming indexes are not an issue.
You can use analytic function count here to eliminate duplicated rows.
select * from (
SELECT m.level4 center, cc.description, m.employeename, m.empno,
TO_DATE (ct.tsdate, 'dd-mon-yyyy') tsdate, ct.starttime, ct.endtime,
ct.DURATION,
NVL (DECODE (ct.paycode, ' ', 'REG', ct.paycode), 'REG') paycode,
ct.titlecode, ct.costcenter, m.tsgroup,
count(1) over (partition by ct.recordtype,ct.empno,ct.tsdate,ct.processdate
order by null) cnt
FROM clairvia_text ct, MASTER m, costcenteroutbound cc
WHERE ct.recordtype = '1'
AND ct.empno = m.empno
AND m.level4 = cc.center
) where cnt=1
I do not have your structures and data, so I run similar queries with all_tab_cols and first query took ~500s on my laptop and second query ~2s.
-- slow test
select count(1)
from all_tab_cols atc
where exists (
select 1
from all_tab_cols atc1
where atc1.column_name = atc.column_name
group by column_name
having count(1) = 1)
-- fast test
select count(1)
from (
select column_name,
count(1) over (partition by atc.column_name order by null) cnt
from all_tab_cols atc
)
where cnt = 1

How to display data with pivot in oracle?

I stuck an hours to display the data in weekly result, but I'm always fail to display that. I'm new in pivot.
Please help me.
Thank you!
Sample data need to produce:
Data,03/10/2014,200,150,186,172
Data,03/16/2014,144,115,181,157
Data,03/22/2014,130,198,136,393
Here's the script:
select 'Data,'''
||(
select listagg(SIM_id,''',''') within group (order by 1)
from (
select distinct substr(dst_channel,1,14) as SIM_id
from table1
where dst_channel like 'SIP/item__-%'
)
)
from dual;
select 'Data'
||','||dtime_day
||','||g1_cnt
||','||g2_cnt
||','||g3_cnt
||','||g4_cnt
from (
select 'Data'
,to_char(d.dtime_day,'MM/dd/yyyy') as dtime_day
,substr(c.dst_channel,1,14) as dst_channel
from table2 d
left join table1
on c.call_date = d.dtime_day
where c.dst_channel like 'SIP/item%'
and c.status like 'ANSWERED%'
and d.dtime_day between trunc(sysdate, 'IW')-(12*7) and trunc(sysdate) -1
)
pivot(
count(dst_channel) as cnt
for dst_channel in (
'SIP/item01' as g1,'SIP/item02' as g2,'SIP/item03' as g3,'SIP/item04' as g4
)
)
order by dtime_day
Result of the query above:
Data,03/10/2014,147,103,86,76
Data,03/11/2014,144,115,81,57
Data,03/12/2014,130,98,59,39

CROSS APPLY too slow for running total - TSQL

Please see my code below as it is running too slowly with the CROSS APPLY.
How can I remove the CROSS APPLY and add something else that will run faster?
Please note I am using SQL Server 2008 R2.
;WITH MyCTE AS
(
SELECT
R.NetWinCURRENCYValue AS NetWin
,dD.[Date] AS TheDay
FROM
dimPlayer AS P
JOIN
dbo.factRevenue AS R ON P.playerKey = R.playerKey
JOIN
dbo.vw_Date AS dD ON Dd.dateKey = R.dateKey
WHERE
P.CustomerID = 12345)
SELECT
A.TheDay AS [Date]
,ISNULL(A.NetWin, 0) AS NetWin
,rt.runningTotal AS CumulativeNetWin
FROM MyCTE AS A
CROSS APPLY (SELECT SUM(NetWin) AS runningTotal
FROM MyCTE WHERE TheDay <= A.TheDay) AS rt
ORDER BY A.TheDay
CREATE TABLE #temp (NetWin money, TheDay datetime)
insert into #temp
SELECT
R.NetWinCURRENCYValue AS NetWin
,dD.[Date] AS TheDay
FROM
dimPlayer AS P
JOIN
dbo.factRevenue AS R ON P.playerKey = R.playerKey
JOIN
dbo.vw_Date AS dD ON Dd.dateKey = R.dateKey
WHERE
P.CustomerID = 12345;
SELECT
A.TheDay AS [Date]
,ISNULL(A.NetWin, 0) AS NetWin
,SUM(B.NetWin) AS CumulativeNetWin
FROM #temp AS A
JOIN #temp AS B
ON A.TheDay >= B.TheDay
GROUP BY A.TheDay, ISNULL(A.NetWin, 0);
Here https://stackoverflow.com/a/13744550/613130 it's suggested to use recursive CTE.
;WITH MyCTE AS
(
SELECT
R.NetWinCURRENCYValue AS NetWin
,dD.[Date] AS TheDay
,ROW_NUMBER() OVER (ORDER BY dD.[Date]) AS RN
FROM dimPlayer AS P
JOIN dbo.factRevenue AS R ON P.playerKey = R.playerKey
JOIN dbo.vw_Date AS dD ON Dd.dateKey = R.dateKey
WHERE P.CustomerID = 12345
)
, MyCTERec AS
(
SELECT C.TheDay AS [Date]
,ISNULL(C.NetWin, 0) AS NetWin
,ISNULL(C.NetWin, 0) AS CumulativeNetWin
,C.RN
FROM MyCTE AS C
WHERE C.RN = 1
UNION ALL
SELECT C.TheDay AS [Date]
,ISNULL(C.NetWin, 0) AS NetWin
,P.CumulativeNetWin + ISNULL(C.NetWin, 0) AS CumulativeNetWin
,C.RN
FROM MyCTERec P
INNER JOIN MyCTE AS C ON C.RN = P.RN + 1
)
SELECT *
FROM MyCTERec
ORDER BY RN
OPTION (MAXRECURSION 0)
Note that this query will work if you have 1 record == 1 day! If you have multiple records in a day, the results will be different from the other query.
As I said here, if you want really fast calculation, put it into temporary table with sequential primary key and then calculate rolling total:
create table #Temp (
ID bigint identity(1, 1) primary key,
[Date] date,
NetWin decimal(29, 10)
)
insert into #Temp ([Date], NetWin)
select
dD.[Date],
sum(R.NetWinCURRENCYValue) as NetWin,
from dbo.dimPlayer as P
inner join dbo.factRevenue as R on P.playerKey = R.playerKey
inner join dbo.vw_Date as dD on Dd.dateKey = R.dateKey
where P.CustomerID = 12345
group by dD.[Date]
order by dD.[Date]
;with cte as (
select T.ID, T.[Date], T.NetWin, T.NetWin as CumulativeNetWin
from #Temp as T
where T.ID = 1
union all
select T.ID, T.[Date], T.NetWin, T.NetWin + C.CumulativeNetWin as CumulativeNetWin
from cte as C
inner join #Temp as T on T.ID = C.ID + 1
)
select C.[Date], C.NetWin, C.CumulativeNetWin
from cte as C
order by C.[Date]
I assume that you could have duplicates dates in the input, but don't want duplicates in the output, so I grouped data before puting it into the table.

Trying to pull a variable only on row with max(date) while summing a different variable on all rows

I need to pull a value from the row that has the maximum date, while also summing all the values of a different column.
What I mean is something like this:
select
a.account_number,
a.client,
a.referral_date,
sum(b.amount),
max(b.date),
case when b.date = max(b.date) then b.due end as due
from a join b on a.account_number = b.account_number
group by a.account_number, a.client, a.referral_date, sum(b.amount), max(b.date), case when b.date = max(b.date) then b.due end
I'm sorry if this doesn't make sense, but I'm trying to sum ALL of "amount" while only getting "due" from the row with the maximum "date".
So if I join them so it only pulls max(date) I won't be able to sum ALL of the amounts.
I've been searching forever for this, but frankly I don't even know what to type into a search engine for this question. Thank you in advance for your help! Let me know how I can further clarify!
Steven
Wouldn't this work:
select a.account_number
, a.client,
, a.referral_date
, sum(b.amount)
, case when b.date = max(b.date) then b.due end as due
from a join b
on a.account_number = b.account_number
group by a.account_number, a.client, a.referral_date
... and if you're using PL\SQL then untested but:
select account_number
, a.client
, a.referral_date
, sum(b.amount)
, max_date
from ( select a.account_number
, a.client,
, a.referral_date
, b.amount
, max(b.date) over ( partition by a.account_number, a.client, a.referral_date ) as max_date
from a join b
on a.account_number = b.account_number )
group by a.account_number, a.client, a.referral_date, max_date
Not sure I precisely understand your goal, but how about this:
SELECT account_number, client, referral_date, amount, due
FROM (SELECT a.account_number,a.client,a.referral_date, b.due, b.date TheDate
, SUM(b.amount) OVER (PARTITION BY b.account_number) amount
, MAX(b.date) OVER (PARTITION BY b.account_number) max_dt
FROM a JOIN b ON a.account_number = b.account_number)
WHERE TheDate = max_dt;

Resources