oracle update from select range - oracle

I use this sql
SELECT *
FROM (
SELECT a.*, rownum rnum
FROM (
SELCT s.id, s.user_id, u.user_id u_id
FROM sample s
INNER JOIN template t ON s.t_id = t.t_id
INNER JOIN user u ON t.u_id = u.u_id
ORDER BY u.u_id desc
) a WHERE rownum <= 100
) WHERE rnum >= 0
The sql query result
id user_id u_id
1 A001 B001
2 A002 B002
3 A003 B003
4 A004 B004
5 A005 B005
May I use one sql for php to update the user_id as u_id's value, not use code to run a loop, thanks.
This is I want the result:
id user_id u_id
1 B001 B001
2 B002 B002
3 B003 B003
4 B004 B004
5 B005 B005

Yes, very simply, without thinking about the complicated update statement, you can use merge ...
merge into sample TGT
using (
-- your original query starts here
SELECT *
FROM (
SELECT a.*, rownum rnum
FROM (
SELECT s.id, s.user_id, u.user_id u_id
FROM sample s
INNER JOIN template t ON s.t_id = t.t_id
INNER JOIN user u ON t.u_id = u.u_id
ORDER BY u.u_id desc
) a WHERE rownum <= 100
) WHERE rnum >= 0
-- your original query ends here
) SRC
on ( TGT.id = SRC.id )
when matched then
update set TGT.user_id = SRC.u_id
;

You want update table sample using table user as source?
merge into sample s
using (select t.t_id, u.user_id
from user u, template t
where u.u_id = t.u_id) u_source
on (u_source.t_id = s.t_id)
when matched then update
set s.user_ud = u_source.user_id

Related

Oracle update invalid identifier on Update

I get an 'invalid identifier' exception when I run the next script.
As I have seen on this link it should work.
It cannot reference to the 'a2' table under the secound selection, but it should update the row with the related value.
update auto a2 set uuid =
(select uuid from (
select c.uuid, c.pk from color c
join sit s on s.pk = c.sit_fk
--where s.auto_fk = auto.pk
join auto m on m.pk = s.auto_fk
where m.pk = a2.pk
group by c.pk, c.uuid
order by c.pk desc
)
where rownum = 1)
Reference the a2 alias in the outer level of the correlated query, rather than the nested level.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE auto ( pk, uuid ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL;
CREATE TABLE color ( pk, uuid, sit_fk ) AS
SELECT 1, 2, 1 FROM DUAL UNION ALL
SELECT 2, 1, 2 FROM DUAL;
CREATE TABLE sit ( pk, auto_fk ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL;
Query 1:
update auto a2 set uuid =
(select uuid from (
select c.uuid, c.pk, m.pk AS apk from color c
join sit s on s.pk = c.sit_fk
--where s.auto_fk = auto.pk
join auto m on m.pk = s.auto_fk
group by m.pk, c.pk, c.uuid
order by m.pk, c.pk desc
)
where rownum = 1
and a2.pk = apk
)
Results:
Query 2:
SELECT *
FROM auto
Results:
| PK | UUID |
|----|------|
| 1 | 2 |
| 2 | 1 |

Oracle double select issue

So I have these 2 tables on Oracle:
CLIENT
cl_id cl_name
1 John
2 Maria
PAYMENTS
pa_id pa_date pa_status cl_id
1 2017-01-01 1 1
2 2017-01-01 1 2
3 2017-02-01 1 1
4 2017-02-01 1 2
5 2017-03-01 0 1
6 2017-03-01 1 2
I need a select statemant that gives me the client ID, NAME and the STATUS of his last payment. So the end result of my select should be:
cl_id cl_name pa_status
1 John 0
2 Maria 1
This is the CLIENT select that works:
select cl_id, cl_name from CLIENT;
This is the last status of the PAYMENT select that works:
select * from (
select pa_status from PAYMENT ORDER BY PA_DATE DESC)
where rownum = 1;
So now, I need to make them work together. I tried 2 ways that didn't work:
select cl_id, cl_name, (select * from (
select pa_status from PAYMENT ORDER BY PA_DATE DESC)
where rownum = 1 and PAYMENT.cl_id = CLIENT.CL_ID) as last_status from CLIENT;
error: invalid identifier
AND this:
select cl_id, cl_name, (select * from (
select pa_status from PAYMENT ORDER BY PA_DATE DESC)
where rownum = 1 ) as last_status from CLIENT;
which don't give me any errors, but only shows the same last status of John that is the last record:
cl_id cl_name last_status
1 John 0
2 Maria 0
Can anyone give me a hint?
Thanks
you need to use analystic function.
This kind of functions let you split your data to some groups, and rank the data for each group as you wish.
In your case:
Select * from (
Select id, name, status, row_number () over (partition by p.cl_id order by p.pa_date desc) as rw
From client c join payments p on p.cl_id = c.cl_id)
Inn where inn.rw = 1;
First take the max of date from for each clientid.
Select cl_id, max(pa_date) as pa_date from PAYMENTS group by cl_id
Now you take ur client table and join with above subquery
select c.cl_id, c.cl_name,
(select pa_status from PAYMENT t where t.pa_date=p.pa_date and t.cl_id=p.cl_id)
from CLIENT c join (Select cl_id, max(pa_date) as pa_date from PAYMENTS group by cl_id) p on p.cl_id=c.cl_id
You can use Oracle's KEEP LAST here:
select cl_id, c.cl_name, last_payment.status
from client
join
(
select
cl_id,
max(pa_status) keep (dense_rank last order by pa_date) as status
from payments
group by cl_id
) last_payment using (cl_id);
(If you want to include clients without payments, change the join to LEFT OUTER JOIN.)
This gets the max date for the client
and then gets the highest payment id with that date.
with max_date as (
select max(date) as max_date, cl_id from payments group by cl_id
)
select c.cl_id, c.cl_name, p.pa_sttus from client c
join payments p
on c.cl_id = p.cl_id
where p.pa_id = (select max(p2.pa_id) from payments p2
join max_date md
on p2.cl_id = md.cl_id
where p.cl_id = p2.cl_id
and p2.pa_date = md.max_date
)

updating two column from select statement?

I am trying to update two columns from select statement, but I am getting error message that says
ORA-00927: missing equal sign
Please, can anyone tell me why ?
UPDATE table1 a
SET co1 ,co2 = (SELECT COUNT (*),
sum(cost)/4
FROM table2 b
WHERE a.customer_id = b.cust_info_customer_id
AND tariff_info = 2);
Since you didn't provide any input:
create table table1(
co1 number,
co2 number,
customer_id number
)
insert into table1 values (1,1, 1)
select * from table1
CO1 CO2 CUSTOMER_ID
-------------------
1 1 1
UPDATE table1 a
SET co1 = (
with table2 as (
select level cost, 1 cust_info_customer_id from dual connect by rownum < 5
)
SELECT COUNT (*)
FROM table2 b
WHERE a.customer_id = b.cust_info_customer_id
)
, co2 = (
with table2 as (
select level cost, 1 cust_info_customer_id from dual connect by rownum < 5
)
SELECT sum(cost)/4
FROM table2 b
WHERE a.customer_id = b.cust_info_customer_id
)
select * from table1
CO1 CO2 CUSTOMER_ID
-------------------
4 2.5 1

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.

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