How to display data with pivot in oracle? - 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

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

Oracle adding a subquery in a CTE

I have the following setup, which works fine and generates output as expected.
I'm trying to add the locations subquery into the CTE so my output will have a random location_id for each row.
The subquery is straight forward and should work but I am getting syntax errors when I try to place it into the 'data's CTE. I was hoping someone could help me out.
CREATE TABLE employees(
employee_id NUMBER(6),
emp_name VARCHAR2(30)
);
INSERT INTO employees(
employee_id,
emp_name
) VALUES
(1, 'John Doe');
INSERT INTO employees(
employee_id,
emp_name
) VALUES
(2, 'Jane Smith');
INSERT INTO employees(
employee_id,
emp_name
) VALUES
(3, 'Mike Jones');
CREATE TABLE locations AS
SELECT level AS location_id,
'Door ' || level AS location_name
FROM dual
CONNECT BY level <=
with rws as (
select level rn from dual connect by level <= 5 ),
data as ( select e.*,round (dbms_random.value(1,5)
) n from employees e)
select employee_id,
emp_name,
trunc (sysdate) + dbms_random.value (0, 5) AS random_date
from rws
join data d on rn <= n
order by employee_id;
-- trying to make this work
with rws as ( select level rn from dual connect by level <= 5 ),
data as ( select e.*, loc.location_id = (
select location_id
from locations order by dbms_random.value()
fetch first 1 row only
),
round (dbms_random.value(1,5)
) n from employees e )
select employee_id,
emp_name,
trunc (sysdate) + dbms_random.value (0, 5) AS random_date
from rws
join data d on rn <= n
order by employee_id;
You need to alias the subquery column expression, rather than trying to assign it to a [variable] name. So instead of this:
with rws as ( select level rn from dual connect by level <= 5 ),
data as ( select e.*, loc.location_id = (
select location_id
from locations order by dbms_random.value()
fetch first 1 row only
),
round (dbms_random.value(1,5)
) n from employees e )
you would do this:
with rws as (
select level rn
from dual
connect by level <= 5
),
data as (
select e.*,
(
select location_id
from locations
order by dbms_random.value()
fetch first 1 row only
) as location_id,
round (dbms_random.value(1,5)) as n
from employees e
)
db<>fiddle
But yes, you'll get the same location_id for each row, which probably isn't what you want.
There are probably better ways to avoid it (or to approach whatever you're actually trying to achieve) but one option is to force the subquery to be correlated by adding something like:
where location_id != -1 * e.employee_id
db<>fiddle
although that might be expensive. It's probably worth asking a new question about that specific aspect.
I am getting the same location_id for every employee_id, which I don't want either.
The subquery is in the wrong place then; move it to the main query, and correlate against both ID and n:
with rws as (
select level rn
from dual
connect by level <= 5
),
data as (
select e.*,
round (dbms_random.value(1,5)) as n
from employees e
)
select d.employee_id,
d.emp_name,
(
select location_id
from locations
where location_id != -1 * d.employee_id * d.n
order by dbms_random.value()
fetch first 1 row only
) as location_id,
trunc (sysdate) + dbms_random.value (0, 5) AS random_date
from rws r
join data d on r.rn <= d.n
order by d.employee_id;
db<>fiddle
Or move the location part to a new CTE, I suppose, with its own row number; and join that on one of your other generated values.

How to avoid Internal_Function by TO_CHAR IN 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'

How to put 2 rows returned from a query into into 2 columns of a single row in Oracle sql

I run a query that returns 2 rows
SELECT a FROM TABLE-A WHERE condition=something;
row1 Value1
row2 Value2
Now, I want to put it in a new table in 2 columns of a single row
SELECT column1, column2 FROM TABLE-B WHERE condition=something;
row1 column1 column2
value1 value2
Can you please help me with this?
It is unclear how you want to pick which row goes into which column - here are a couple of options:
SELECT MIN( a ) AS minimum_value,
MAX( a ) AS maximum_value
FROM table_a
WHERE 1=1;
or
SELECT MAX( a ) KEEP ( DENSE_RANK FIRST ORDER BY ROWNUM ) AS first_value,
MAX( a ) KEEP ( DENSE_RANK LAST ORDER BY ROWNUM ) AS last_value
FROM table_a
WHERE 1=1;
or
SELECT *
FROM ( SELECT a, ROWNUM AS rn FROM table_a WHERE 1=1 )
PIVOT ( MAX(a) FOR rn IN ( 1 AS first_value, 2 AS second_value ) );

How do I get rowcount of a cte in a separate dataset?

I have identified a way to get fast paged results from the database using CTEs and the Row_Number function, as follows...
DECLARE #PageSize INT = 1
DECLARE #PageNumber INT = 2
DECLARE #Customer TABLE (
ID INT IDENTITY(1, 1),
Name VARCHAR(10),
age INT,
employed BIT)
INSERT INTO #Customer
(name,age,employed)
SELECT 'bob',21,1
UNION ALL
SELECT 'fred',33,1
UNION ALL
SELECT 'joe',29,1
UNION ALL
SELECT 'sam',16,1
UNION ALL
SELECT 'arthur',17,0;
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total = ( SELECT
Count( id )
FROM cteCustomers )
FROM cteCustomers
INNER JOIN #Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
Using this technique the returned results is really really fast even on complex joins and filters.
To perform paging I need to know the Total Rows returned by the full CTE. I have "Bodged" this by putting a column that holds it
Total = ( SELECT
Count( id )
FROM cteCustomers )
Is there a better way to return the total in a different result set without bodging it into a column? Because it's a CTE I can't seem to get it into a second result set.
Without using a temp table first, I'd use a CROSS JOIN to reduce the risk of row by row evaluation on the COUNT
To get total row, this needs to happen separately to the WHERE
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total
FROM cteCustomers
INNER JOIN #Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
CROSS JOIN
(SELECT Count( *) AS Total FROM cteCustomers ) foo
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
However, this isn't guaranteed to give accurate results as demonstrated here:
can I get count() and rows from one sql query in sql server?
Edit: after a few comments.
How to avoid a CROSS JOIN
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row,
COUNT(*) OVER () AS Total --the magic for this edit
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total
FROM cteCustomers
INNER JOIN #Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
Note: YMMV for performance depending on 2005 or 2008, Service pack etc
Edit 2:
SQL Server Central shows another technique where you have reverse ROW_NUMBER. Looks useful
#Digiguru
OMG, this really is the wholy grail!
WITH cteCustomers
AS ( SELECT id,
Row_Number() OVER(ORDER BY Age DESC) AS Row,
Row_Number() OVER(ORDER BY id ASC)
+ Row_Number() OVER(ORDER BY id DESC) - 1 AS Total /*<- voodoo here*/
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT name, age, Total
/*This is where I choose the columns I want to read, it returns really fast!*/
FROM cteCustomers
INNER JOIN #Customer cust
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
So obvious now.

Resources