Oracle Query Tuning (Duplicate table access) - oracle

I'm trying to further tune this query. The query returns the status for three different tests for each sample. However, if I wish to further filter the samples returned, I have to put the conditions in both 'SELECT ... FROM sample ...' queries.
Can this query be rewritten referencing the sample table only once?
SELECT sample_id,
created_on,
s_acid,
s_ph,
s_titr
FROM
(SELECT sample_id, -- Rows w/ same sample_id to columns
MAX (CASE WHEN tst_tmpl_id = 36 THEN status END) AS s_acid,
MAX (CASE WHEN tst_tmpl_id = 43 THEN status END) AS s_ph,
MAX (CASE WHEN tst_tmpl_id = 66 THEN status END) AS s_titr
FROM (SELECT test_id, test_tmpl_id, sample_id FROM test)
JOIN (SELECT sample_id FROM sample WHERE sam_tmpl_id = 18)
USING (sample_id)
GROUP BY sample_id
)
-- get other sample fields
JOIN (SELECT sample_id, created_on FROM sample WHERE sam_tmpl_id = 18)
USING (sample_id)

WITH q AS
(
SELECT sample_id, created_on
FROM sample
WHERE sam_tmpl_id = 18
)
SELECT sample_id,
created_on,
s_acid,
s_ph,
s_titr
FROM (
SELECT sample_id, -- Rows w/ same sample_id to columns
MAX (CASE WHEN tst_tmpl_id = 36 THEN status END) AS s_acid,
MAX (CASE WHEN tst_tmpl_id = 43 THEN status END) AS s_ph,
MAX (CASE WHEN tst_tmpl_id = 66 THEN status END) AS s_titr
FROM (
SELECT test_id, test_tmpl_id, sample_id
FROM test
)
JOIN q
USING (sample_id)
GROUP BY
sample_id
)
JOIN q
USING (sample_id)

Related

Pl / SQL Oracle helps to run a Date in Subquery

How could I get the date of the Maximum Value, by means of a subquery
I can't put the Date in the Main query because I would have to add it to the group by it would bring me a lot of data
Here is the Code:
SELECT MAX (A1.VALOR) AS VALOR,
(SELECT sq1.FECHA
FROM VARIABLE_VALORES_SMEC sq1
WHERE sq1.ID_AGENTE = A1.ID_AGENTE)
MES, -- {<-- Here is the Problem}
(SELECT CODIGO_AGENTE
FROM AGENTES
WHERE ID_AGENTE = A1.ID_AGENTE)
Agentess,
(SELECT NOMBRE_AGENTE
FROM AGENTES
WHERE ID_AGENTE = A1.ID_AGENTE)
Nombre_Agente
FROM VARIABLE_VALORES_SMEC A1
WHERE A1.VALOR < '1'
AND A1.VALOR != '0'
AND A1.ID_AGENTE IN (SELECT C1.ID_AGENTE
FROM VARIABLE_VALORES_SMEC C1
WHERE A1.FECHA = C1.FECHA)
AND A1.ID_AGENTE IN (SELECT B1.ID_AGENTE
FROM AGENTES B1
WHERE ID_CATEGORIA_AGENTE = 'AC006')
AND (A1.FECHA BETWEEN (ADD_MONTHS (TO_DATE ( :FECHAIN, 'MM/DD/YYYY'),
-1))
AND (LAST_DAY (
ADD_MONTHS (
TO_DATE ( :FECHAIN, 'MM/DD/YYYY'),
-1))))
AND A1.ID_VARIABLE LIKE '%_calc_total_pot#%'
GROUP BY ID_AGENTE
Am I correct that you need (fecha) for maximum A1.VALOR?
If - yes, you can use the following query, or if - no, just replace A1.VALOR with the required column in keep() clause:
SELECT MAX (A1.VALOR) AS VALOR,
max(A1.FECHA)keep(dense_rank first order by A1.VALOR desc) MES, -- A1.VALOR is used here as sort key, replace it with what you want
(SELECT CODIGO_AGENTE
FROM AGENTES
WHERE ID_AGENTE = A1.ID_AGENTE)
Agentess,
(SELECT NOMBRE_AGENTE
FROM AGENTES
WHERE ID_AGENTE = A1.ID_AGENTE)
Nombre_Agente
FROM VARIABLE_VALORES_SMEC A1
WHERE A1.VALOR < '1'
AND A1.VALOR != '0'
AND A1.ID_AGENTE IN (SELECT C1.ID_AGENTE
FROM VARIABLE_VALORES_SMEC C1
WHERE A1.FECHA = C1.FECHA)
AND A1.ID_AGENTE IN (SELECT B1.ID_AGENTE
FROM AGENTES B1
WHERE ID_CATEGORIA_AGENTE = 'AC006')
AND (A1.FECHA BETWEEN (ADD_MONTHS (TO_DATE ( :FECHAIN, 'MM/DD/YYYY'),
-1))
AND (LAST_DAY (
ADD_MONTHS (
TO_DATE ( :FECHAIN, 'MM/DD/YYYY'),
-1))))
AND A1.ID_VARIABLE LIKE '%_calc_total_pot#%'
GROUP BY ID_AGENTE
You can use row_number analytical function to fetch one record for which value is highest and use the fecha of that record. Use following sub query:
(Select fecha from
(SELECT sq1.FECHA, row_number() over (order by sq1.value desc nulls last) as rn
FROM VARIABLE_VALORES_SMEC sq1
WHERE sq1.ID_AGENTE = A1.ID_AGENTE)
Where rn = 1) MES

How to use 'EXIST' in a simple oracle query

I have a table called ‘MainTable’ with following data
Another table called ‘ChildTable’ with following data (foreighn key Number)
Now I want to fetch those records from ‘ChildTable’ if there exists at least one ‘S’ status.
But if any other record for this number id ‘R’ then I don’t want to fetch it
Something like this-
I tried following
Select m.Number, c.Status from MainTable m, ChildTable c
where EXISTS (SELECT NULL
FROM ChildTable c2
WHERE c2.status =’S’ and c2.status <> ‘R’
AND c2.number = m.number)
But here I am getting record having ‘R’ status also, what I am doing wrong?
You can try something like this
select num, status
from
(select id, num, status,
sum(decode(status, 'R', 1, 0)) over (partition by num) Rs,
sum(decode(status, 'S', 1, 0)) over (partition by num) Ss
from child_table) t
where t.Rs = 0 and t.Ss >= 1
-- and status = 'S'
Here is a sqlfiddle demo
The child records with 'R' might be associated with a maintable record that also has another child record with status 'S' -- that is what your query is asking for.
Select
m.Number,
c.Status
from MainTable m
join ChildTable c on c.number = m.number
where EXISTS (
SELECT NULL
FROM ChildTable c2
WHERE c2.status =’S’
AND c2.number = m.number) and
NOT EXISTS (
SELECT NULL
FROM ChildTable c2
WHERE c2.status =’R’
AND c2.number = m.number)
WITH ChildrenWithS AS (
SELECT Number
FROM ChildTable
WHERE Status = 'S'
)
,ChildrenWithR AS (
SELECT Number
FROM ChildTable
WHERE Status = 'R'
)
SELECT MaintTable.Number
,ChildTable.Status
FROM MainTable
INNER JOIN ChildTable
ON MainTable.Number = ChildTable.Number
WHERE MainTable.Number IN (SELECT Number FROM ChildrenWithS)
AND MainTable.Number NOT IN (SELECT Number FROM ChildrenWithR)

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.

Retrieving page that contains specific row

I have a procedure that returns multiple rows on some criteria and in specific order. These rows are separated into few pages (50 rows per page).
How can I retrieve all rows from page having some specific row.
I've created a query the query that do this work, but it is not optimized and have huge impact on performance. Help me please to optimize it or give an alternative to it:
select *
from
(
select file_id, row_number() over (order by rownum) rn
from my_table
)
where trunc(rn/50) = (
select trunc(rn/50) from
(select t.*, rownum rn from my_table t)
where file_id = 29987);
You have to adjust it a little...
with tab as
(
-- the
-- row_number() over (order by rownum) rn
-- should be here
select level + 1000 as val
, level/50 as rn_50
from dual
connect by
level < 140
)
, val as
(
select rn_50
from tab
where val = 1004 -- pg 1
--where val = 1051 -- pg 2
--where val = 1101 -- pg 3
)
select *
from tab t
where rn_50 >= (select floor(rn_50) from val)
and rn_50 <= (select ceil (rn_50) from val)
;

Query to exclude row based on another row's filter

I'm using Oracle 10g.
Question: How can I write query to return just ID only if ALL the codes for that ID end in 6? I don't want ID=1 because not all its codes end in 6.
TABLE_A
ID Code
===============
1 100
1 106
2 206
3 316
3 326
4 444
Desired Result:
ID
==
2
3
You simply want each ID where the count of rows for that id is the same as the count of rows where the third digit is six.
SELECT ID
FROM TABLE_A
GROUP BY ID
HAVING COUNT(*) = COUNT(CASE WHEN SUBSTR(code,3,1) = '6' THEN 1 END)
Try this:
SELECT DISTINCT b.id
FROM (
SELECT id,
COUNT(1) cnt
FROM table_a
GROUP BY id
) a,
(
SELECT id,
COUNT(1) cnt
FROM table_a
WHERE CODE LIKE '%6'
GROUP BY id
)b
WHERE a.id = b.id
AND a.cnt = b.cnt
Alternative using ANALYTIC functions:
SELECT DISTINCT id
FROM
(
SELECT id,
COUNT(1) OVER(PARTITION BY id) cnt,
SUM(CASE WHEN code LIKE '%6' THEN 1 ELSE 0 END) OVER(PARTITION BY id) sm
FROM table_a
)
WHERE cnt = sm

Resources