Multiple statements with one cte? - sql

What is the correct syntax for the following? (I need these in one query)
--- 1. task
update A set .... where ....
insert into A (...) values (...);
--- 2 .task
With cte as (select A.column...)
update A set ... if condition1(includes cte table)
update A set ... if condition2(includes cte table)
update A set ... if condition3(includes cte table)
In words:
I update table A or insert into it
After that I refer to this updated TableA in a cte table, which contains a ROW_NUMBER function,
And then I want to update TableA again depended on that rownumber from CTE in a specific row, for example: if rownumber value in the CTE is 1, do this, if it is max(rownumber) for that specific row then do that....
I read that cte-s only persist for a single statement. I tried to copy the cte for every update statement, separated with semicolons, but that didn't work. I read about MERGE but I'm not sure if this is the right way for that. Is it the OUTPUT clause, if yes, how to use it? Or something else? Can you help me please?

You don't need multiple update statement. You can do with one.
; WITH CTE as ( select . . . )
UPDATE A
SET col1 = case when .... then new_1 else col1 end,
col2 = case when .... then new_2 else col2 end
FROM CTE as A

For anyone who tries to avoid copy-paste and wants to reuse some complex query from CTE to perform multiple statements, probably temporary table would work for you:
select A.column as AnyColumn, ... into #NameYouWant from A, ...
update A set column1 = (select AnyColumn from #NameYouWant where ...)
update B set column2 = (select AnyColumn from #NameYouWant where ...)
update C set column3 = (select AnyColumn from #NameYouWant where ...)
Please note that number sign (#) in table name is important, it will indicate that table is temporary, so it would persist only within your current session.

Correct, a CTE only persists in the statement it exists in. Therefore a statement like the following will fail:
WITH CTE AS(
SELECT *,ROW_NUMBER() OVER (PARTITION BY Date ORDER BY ID) AS RN
FROM YourTable)
SELECT *
FROM CTE
WHERE RN = 1;
SELECT *
FROM CTE
WHERE RN = 2;
That's because the CTE no longer exists during the second statement.
For an UPDATE (or any statement ) you'll therefore need to redeclare your CTE each time. Thus, using the example above, you would have to do:
WITH CTE AS(
SELECT *,ROW_NUMBER() OVER (PARTITION BY Date ORDER BY ID) AS RN
FROM YourTable)
SELECT *
FROM CTE
WHERE RN = 1;
WITH CTE AS(
SELECT *,ROW_NUMBER() OVER (PARTITION BY Date ORDER BY ID) AS RN
FROM YourTable)
SELECT *
FROM CTE
WHERE RN = 2;
If the expressions within the CTE are quite complex, and you use them often, you might instead consider using a VIEW.

Related

Oracle: UPDATE with ORDER BY [duplicate]

I want to populate a table column with a running integer number, so I'm thinking of using ROWNUM. However, I need to populate it based on the order of other columns, something like ORDER BY column1, column2. That is, unfortunately, not possible since Oracle does not accept the following statement:
UPDATE table_a SET sequence_column = rownum ORDER BY column1, column2;
Nor the following statement (an attempt to use WITH clause):
WITH tmp AS (SELECT * FROM table_a ORDER BY column1, column2)
UPDATE tmp SET sequence_column = rownum;
So how do I do it using an SQL statement and without resorting to cursor iteration method in PL/SQL?
This should work (works for me)
update table_a outer
set sequence_column = (
select rnum from (
-- evaluate row_number() for all rows ordered by your columns
-- BEFORE updating those values into table_a
select id, row_number() over (order by column1, column2) rnum
from table_a) inner
-- join on the primary key to be sure you'll only get one value
-- for rnum
where inner.id = outer.id);
OR you use the MERGE statement. Something like this.
merge into table_a u
using (
select id, row_number() over (order by column1, column2) rnum
from table_a
) s
on (u.id = s.id)
when matched then update set u.sequence_column = s.rnum
UPDATE table_a
SET sequence_column = (select rn
from (
select rowid,
row_number() over (order by col1, col2)
from table_a
) x
where x.rowid = table_a.rowid)
But that won't be very fast and as Damien pointed out, you have to re-run this statement each time you change data in that table.
First Create a sequence :
CREATE SEQUENCE SEQ_SLNO
START WITH 1
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER;
after that Update the table using the sequence:
UPDATE table_name
SET colun_name = SEQ_SLNO.NEXTVAL;
A small correction just add AS RN :
UPDATE table_a
SET sequence_column = (select rn
from (
select rowid,
row_number() over (order by col1, col2) AS RN
from table_a
) x
where x.rowid = table_a.rowid)

Select count(*) from table where (multiple id) in (table)

Is there a way to write
SELECT count(*) from tablename where (multiple_ids_here) in (SELECT id from tablename)
Normally, I would write:
select count(*) from tablename
where id_1 in (SELECT id from tablename)
OR id_2 in (SELECT id from tablename)
id_3 in (SELECT id from tablename)
which very inefficient if we have multiple values.
Anyone?
EDIT: Question updated. What if I want to select count?
Your version with three ins is probably the most efficient way of doing this. If you want a comparison to try, you can use exists:
select . . .
from t t1
where exists (select 1
from tablename t2
where t2.id in (t1.id_1, t1.id_2, t1.id_3)
);
I should also note that storing ids in multiple columns like this is usually a sign of a problem with the data model. You probably want a table with one row per id, rather than one column per id. Such a format would also simplify this type of query.
For the updated question regarding getting a count(*)... using cross apply() with values() to unpivot your data in a common table expression:
;with cte as (
select t.Id, v.RelatedId
from t
cross apply (values (id_1),(id_2),(id_3)) v(RelatedId)
)
select
cte.Id
, RelationCount = count(*)
from cte
inner join RelatedTable r
on cte.RelatedId = r.Id
group by cte.Id
I am not sure i understand your question could you give an example of the data you are using and the out come.
From what i understand you could use a cte like this .
;WITH Sales_CTE ([counts],CustomerID, SalespersonPersonID,PickedByPersonID)
AS
(
select count(*),CustomerID,SalespersonPersonID ,PickedByPersonID
from [WideWorldImporters].[Sales].[Orders]
group by CustomerID,SalespersonPersonID,PickedByPersonID
)
SELECT sum([counts])
FROM Sales_CTE
GO
It would give you a result like this . You would jsut have to change the columns around .

Using UPDATE to insert a value returned by a SELECT statement

I have a table where I want to update a column with the value in another column in the previous row.
I want to do something like this.
UPDATE myTable as b SET prev=(SELECT top 1 myField FROM myTable
WHERE rowID<b.rowID ORDER By rowID Desc)
Any ideas?
Note: I'm using Access/SQL server. I am getting an error 'operation must use an updatable query'.
Note2: The solution from How do I UPDATE from a SELECT in SQL Server? doesnt work.
You can achieve this with a combination of the LAG() analytic function to compute the value of for the previous row and an UPDATE (here, I'm using a MERGE statement):
MERGE INTO mytable as target using (
select
t.id,
lag(field1) over (partition by null order by id) new_prev_field
from mytable t
) as src (id, new_prev_field)
on (target.id = src.id)
when matched
then update
set prev_field1 = src.new_prev_field;
SQL Fiddle
Instead of Top, use Max()
UPDATE myTable as b
SET prev = (
SELECT max(rowID)
FROM myTable
WHERE rowID < b.rowID)

Oracle: Updating a table column using ROWNUM in conjunction with ORDER BY clause

I want to populate a table column with a running integer number, so I'm thinking of using ROWNUM. However, I need to populate it based on the order of other columns, something like ORDER BY column1, column2. That is, unfortunately, not possible since Oracle does not accept the following statement:
UPDATE table_a SET sequence_column = rownum ORDER BY column1, column2;
Nor the following statement (an attempt to use WITH clause):
WITH tmp AS (SELECT * FROM table_a ORDER BY column1, column2)
UPDATE tmp SET sequence_column = rownum;
So how do I do it using an SQL statement and without resorting to cursor iteration method in PL/SQL?
This should work (works for me)
update table_a outer
set sequence_column = (
select rnum from (
-- evaluate row_number() for all rows ordered by your columns
-- BEFORE updating those values into table_a
select id, row_number() over (order by column1, column2) rnum
from table_a) inner
-- join on the primary key to be sure you'll only get one value
-- for rnum
where inner.id = outer.id);
OR you use the MERGE statement. Something like this.
merge into table_a u
using (
select id, row_number() over (order by column1, column2) rnum
from table_a
) s
on (u.id = s.id)
when matched then update set u.sequence_column = s.rnum
UPDATE table_a
SET sequence_column = (select rn
from (
select rowid,
row_number() over (order by col1, col2)
from table_a
) x
where x.rowid = table_a.rowid)
But that won't be very fast and as Damien pointed out, you have to re-run this statement each time you change data in that table.
First Create a sequence :
CREATE SEQUENCE SEQ_SLNO
START WITH 1
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER;
after that Update the table using the sequence:
UPDATE table_name
SET colun_name = SEQ_SLNO.NEXTVAL;
A small correction just add AS RN :
UPDATE table_a
SET sequence_column = (select rn
from (
select rowid,
row_number() over (order by col1, col2) AS RN
from table_a
) x
where x.rowid = table_a.rowid)

Selecting a given row in a table

here is a simple problem. I have a table of 500 rows and what to be able to select a given row number n. This is what I am doing:
select *
from table
where table.arg1 ='A'
and time_stamp=to_date('1/8/2010','MM/DD/YYYY')
and rownum = n
But it would only work for the row 1, for the rest it doesn't return anything. Any idea?
The reason why where rownum = 3 returns an empty rowset is that the condition is not true for the first row. For the second row, there is still no first row in the resultset, and rownum is still 1. So the condition fails again. See this page for a more detailed explanation.
You can use row_number() in a subquery:
select *
from (
select row_number() over (order by col1) as rn, yt.*
from YourTable yt
) sub
where rn = 3
Or even simpler, but perhaps more confusing, using rownum itself:
select *
from (
select rownum as rn, yt.*
from YourTable yt
) sub
where rn = 3