I want to take the result of a CTE query and use it inside another query.
This simplified example uses a CTE query to return a list of ids.
with test_cte (id,name) as (
select id, name
from test
)
select id
from test_cte
where name = 'john'
I want to use this list of ids to delete some records like this but I'm getting a syntax error:
delete from test
where id in (
with test_cte (id,name) as (
select id, name
from test
)
select id
from test_cte
where name = 'john'
)
Is there a way to do this?
Do you just mean this:
;with test_cte(id,name) as
(
select id,name from dbo.test
)
delete test_cte where name='john';
Do you want to delete rows and show the rows you deleted?
;with test_cte(id,name) as
(
select id,name from dbo.test
)
delete test_cte
output deleted.id, deleted.name
where name='john';
Example db<>fiddle
To take your explicit example:
delete from test
where id in (
with test_cte (id,name) as (
select id, name
from test
)
select id
from test_cte
where name = 'john'
)
You're getting a syntax error because, well, there's an error in your syntax. CTE must be defined up front, not in any random or arbitrary point in your query.
;with test_cte (id,name) as (
select id, name
from test
)
delete from test
where id in (
select id
from test_cte
where name = 'john'
)
But this still seems awfully over-complicated compared to the simpler examples I've shown.
Related
I want to insert new data into the STG.NEW_TABLE when the id does not already exist in that table.
Since we can't use insert statements in dbt, I am not sure how to convert this query for dbt:
insert into STG.NEW_TABLE
select FILE_NAME::string as file_name,
id::string as id,
XMLGET(f.value,'value'):"$"::string as val
from STG.OLD_TABLE ot,
lateral flatten(input => XMLGET( ot.XML_DATA, 'data' ):"$") f
where not exists (select id
from stg.NEW_TABLE nt
where nt.id=ot.id);
I tried breaking it into two parts where I first flatten the xml into a TMP view and then do a union in another model:
(SELECT * FROM {{ref('TMP_VIEW')}} where id NOT IN
(SELECT id
FROM STG.STG.NEW_TABLE))
UNION ALL
SELECT * FROM STG.NEW_TABLE
as suggested here: https://docs.getdbt.com/guides/migration/tools/migrating-from-stored-procedures/2-inserts
While it seems to work, it doesn't feel like the most efficient solution. I do not want to split the process into two parts. I am gussing something with CTEs might be better. Any recommendations?
In dbt you can set the materialization of a table to be incremental in the schema.yml file or within the sql file itself with:
{{ config(materialized='incremental', dist='id') }}
https://docs.getdbt.com/docs/build/materializations#incremental
This will only insert records that don't already exist within the table
edit based on comments
What you can do is union the new and old table, then rank the id to retrieve a unique one. This allows the model to be incremental, and by specifying which data comes from the old/new table you can ensure only new records are inserted.
{{ config(materialized='incremental', dist='id') }}
with cte as (
select *, 0 oldtable from stg.NEW_TABLE
union
select FILE_NAME::string as file_name,
id::string as id,
XMLGET(f.value,'value'):"$"::string as val,
1 oldtable
from STG.OLD_TABLE ot,
lateral flatten(input => XMLGET( ot.XML_DATA, 'data' ):"$") f
where not exists (select id
from stg.NEW_TABLE nt
where nt.id=ot.id)
)
final as (
select *,
row_number() OVER ( order by oldtable, id ) rn
from cte
)
select
file_name,
id,
val
from final
where rn = 1
and oldtable = 1
Tables: I have 3 tables
They are cust, new_cust, old_cust
all of them have 3 columns, they are id, username, name
each of them have possibilities to have same data as the others.
I would like to make "whole" table that consisting all of them but only the uniques.
I've Tried
Creating a dummy table
I've tried to create the dummy table called "Temp" table by
select *
into Temp
from cust
insert all table to dummy
Then I insert all of them into they Temp table
insert into temp
select * from new_cust
insert into temp
select * from old_cust
taking uniques using distinct
After they all merged I'm using distinct to only take the unique id value
select distinct(id), username, fullname
into Whole
from temp
it did decreasing some rows
Result
But after I move it to whole table I would like to put primary key on id but I got the message that there are some duplicate values. Is there any other way?
I am guessing that you want unique ids. And you want these prioritized by the tables in some order. If so, you can do this with union all and row_number():
select id, username, name
from (select c.*,
row_number() over (partition by id order by priority) as seqnum
from ((select id, username, name, 1 as priority
from new_cust
) union all
(select id, username, name, 2 as priority
from cust
) union all
(select id, username, name, 3 as priority
from old_cust
)
) c
) c
where seqnum = 1;
Try this:
insert into temp
select * from new_cust
UNION
select * from old_cust
Union will avoid the duplicate entries and you can then create a primary key on ID column
Try this below query...
WITH cte as (
SELECT id, username, NAME,
ROW_NUMBER() OVER (PARTITION BY t1.id ORDER BY t1.username, t1.name ) AS rn
FROM cust t1
LEFT JOIN new_cust t2 ON t1.Id = t2.Id
LEFT JOIN old_cust t3 ON t2.Id = t3.Id
)
SELECT id, username, NAME
FROM cte
WHERE rn = 1
Note:-
Put all the query inside a CTE(Common table expression)
with a new column(rn) that you will use to filter the results.
This new Column will produce ROW_NUMBER()....PARTITION BY username,name.....
But after I move it to whole table I would like to put primary key on
id but I got the message that there are some duplicate values.?
That's because You are trying to insert ID value from each of the tables to Whole table.
Just insert username and name and skip ID. ID is IDENTITY and it MUST be unique.
Run this on Your current Whole table to see if You have duplicated Id's:
select COUNT(ID), username
from whole
GROUP BY username
HAVING COUNT(ID) > 1
To get unique customers recreate table Whole and make ID col IDENTITY:
IF OBJECT_ID ('dbo.Whole') IS NOT NULL DROP TABLE dbo.Whole;
CREATE TABLE Whole (ID INT NOT NULL IDENTITY(1,1), Name varchar(max), Username varchar(max))
Insert values into Whole table:
INSERT INTO Whole
SELECT Name, Username FROM cust
UNION
SELECT Name, Username FROM new_cust
UNION
SELECT Name, Username FROM old_cust
Make ID col PK.
What does Unique mean for your row ?
If it is only the username, and you don't care about keeping the old ID values,
this will favor the new_cust data over the old_cust data.
SELECT
ID = ROW_NUMBER() OVER (ORDER BY all_temp.username)
, all_temp.*
INTO dbo.Temp
FROM
(
SELECT nc.username, nc.[name] FROM new_cust AS nc
UNION
SELECT oc.username, oc.[name]
FROM old_cust AS oc
WHERE oc.username NOT IN (SELECT nc1.username FROM new_cust AS nc1) --remove the where part if needed
) AS all_temp
ALTER TABLE dbo.Temp ALTER COLUMN ID INTEGER NOT NULL
ALTER TABLE dbo.Temp ADD PRIMARY KEY (ID)
If by Unique you mean both the username and name then just remove the where part in the union
If we use CTE for removing duplicates, delete from cte works -that is deletes rows from original table. How it happens?
If we update CTE for some value, that is not reflected in original table, why it is so?
For eg:
CREATE TABLE TABLE_DUPLICATEREMOVALTEST
(
ID INT,NAME VARCHAR(20)
)
INSERT INTO TABLE_DUPLICATEREMOVALTEST
SELECT 1,'hina'
union all
SELECT 1,'hina'
union all
SELECT 1,'hina'
union all
SELECT 2,'Tina'
union all
SELECT 3,'Mina'
union all
SELECT 4,'Lina'
union all
SELECT 5,'Sina'
-- To test update
alter table TABLE_DUPLICATEREMOVALTEST
add Row_no INT
;WITH CTE (rNO,Row_no)
AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY ID,NAME ORDER BY ID, NAME) AS rNO, id
FROM TABLE_DUPLICATEREMOVALTEST
)
UPDATE CTE SET Row_no = 100 WHERE Rno = 1 ---Not updating column in the main table
--DELETE FROM CTE WHERE Rno ! = 1 ---but this removes rows from main table
SELECT * FROM TABLE_DUPLICATEREMOVALTEST
Can anyone help? I googled it but never found anything satisfactory..
It is often convenient in PosgreSQL to create "tables" on the fly so to refer to them, e.g.
with
selected_ids as (
select 1 as id
)
select *
from someTable
where id = (select id from selected_ids)
Is it impossible to provide multiple values as id this way? I found this answer that suggests using values for similar problem, but I have problem with translating it to the example below.
I would like to write subqueries such as
select 1 as id
union
select 2 as id
union
select 7 as id
or
select 1 as id, 'dog' as animal
union
select 7 as id, 'cat' as animal
in more condensed way, without repeating myself.
You can use arguments in the query alias:
with selected_ids(id) as (
values (1), (3), (5)
)
select *
from someTable
where id = any (select id from selected_ids)
You can also use join instead of a subquery, example:
create table some_table (id int, str text);
insert into some_table values
(1, 'alfa'),
(2, 'beta'),
(3, 'gamma');
with selected_ids(id) as (
values (1), (2)
)
select *
from some_table
join selected_ids
using(id);
id | str
----+------
1 | alfa
2 | beta
(2 rows)
You can pass id and animal field in WITH like this
with selected_ids(id,animal) as (
values (1,'dog'), (2,'cat'), (3,'elephant'),(4,'rat')--,..,.. etc
)
select *
from someTable
where id = any (select id from selected_ids)
You should use union and IN statement like this:
with
selected_ids as (
select 1 as id
union
select 2 as id
union
select 3 as id
....
)
select *
from someTable
where id in (select id from selected_ids)
after reviewing wingedpanther's idea and looking for it, you can use his idea IF those id's are continuously like this:
with
selected_ids as (
SELECT * FROM generate_series(Start,End) --(1,10) for example
)
select *
from someTable
where id in (select id from selected_ids)
If they are not continuously , the only way you can do that is by storing those ID's in a different table(maybe you have it already and if not insert it)
And then:
select *
from someTable
where id in (select id from OtherTable)
I'll start directly by explaining with an example. Suppose I have a table which has 3 columns as shown.
Now what I am trying to achieve is, I want the first values of each individual column into a single column. So it would be something like this,
I have tried a few queries here including using TOP 1 and other incorrect ways. But I am still missing something here to achieve the exact output.
Need some guidance here on how to achieve this. Thank you.
SAMPLE TABLE
SELECT * INTO #TEMP
FROM
(
SELECT 1 BATCH_ID,'AAA' ASSIGNMENTTITLE,'FILE' ASSIGNMENTTYPE
UNION ALL
SELECT 1,'AAA1','FILE'
UNION ALL
SELECT 1,'AAA','FILE'
)TAB
If you need the second row specifically you can do the below
QUERY
;WITH CTE AS
(
-- Order row according to default format
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT(0))) RNO,*
FROM #TEMP
)
SELECT CAST(BATCH_ID AS VARCHAR(20)) FROM CTE WHERE RNO=2
UNION ALL
SELECT ASSIGNMENTTITLE FROM CTE WHERE RNO=2
UNION ALL
SELECT ASSIGNMENTTYPE FROM CTE WHERE RNO=2
Click here to view result
UPDATE
Since there are 3 items in each record, it can be puzzled unless and otherwise an a column is for each items in a record.
;WITH CTE AS
(
-- Order row according to default format
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT(0))) RNO,*
FROM #TEMP
)
SELECT CAST(BATCH_ID AS VARCHAR(20)),RNO
FROM CTE
UNION ALL
SELECT ASSIGNMENTTITLE,RNO
FROM CTE
UNION ALL
SELECT ASSIGNMENTTYPE,RNO
FROM CTE
ORDER BY RNO
Click here to view result
You can use the concat() function to create a column consisting of all the desired values
More info here
Simply you can try this. If want specific for a row use rowid. For all columns Use unpivot
create table #temp(id int, name varchar(100), title varchar(100))
insert into #temp values(1,'aaa','file')
insert into #temp values(1,'aaas','filef')
insert into #temp values(1,'aaaww','filefs')
select * from #temp
select top 1 cast(id as varchar) title from #temp
union
select top 1 name from #temp
union
select top 1 title from #temp
drop table #temp
This might help you
select top 1 convert(varchar(10), batch_id) ASSIGNMENTTITLE from table
union all
select top 1 ASSIGNMENTTITLE from table
union all
select top 1 ASSIGNMENTTYPE from table
If this is really what you want: "I want the first values of each individual column into a single column" it would be:
select ASSIGNMENTTITLE
from (
select min(convert(varchar(10), batch_id)) ASSIGNMENTTITLE,
1 ColOrder from table
union all
select min(ASSIGNMENTTITLE),
2 ColOrder from table
union all
select min(ASSIGNMENTTYPE),
3 ColOrder from table
) as data
order by ColOrder