How to prevent CTE execution until reached OR NOT EXISTS - sql-server

I have an SQL Server query that uses some CTEs (Common Table Expressions). It has two tables that it selects the data from. The two tables are identical in structure, but not necessary data. The query will first select from table_a and if no rows are fetched, it then selects from table_b. The query is something like this:
;WITH cte_a AS (
...
), cte_b AS (
...
)
SELECT *
FROM table_a
INNER JOIN cte_a ON condition_a
OR NOT EXISTS (
SELECT *
FROM table_b
INNER JOIN cte_b ON condition_b
)
The current problem that I have is that cte_b will always be executed regardless of whether table_a returns any rows. This is not very ideal for me; I would like to have cte_b execute if and only if the subquery for table_a returns no rows.
I tried moving the cte_b to be just before the subquery for table_b as
;WITH cte_a AS (
...
)
SELECT *
FROM table_a
INNER JOIN cte_a ON condition_a
OR NOT EXISTS (
;WITH cte_b AS (
...
)
SELECT *
FROM table_b
INNER JOIN cte_b ON condition_b
)
However, the IDE complains. I think that this wasn't the way CTEs are supposed to be used.

Create temporary table for storing the data - split the query to two separate INSERT statements, where the second is executed only in no data is populated after first query is completed. Something like this:
CREATE TABLE #TEmp
(
);
;WITH cte_a AS (
...
)
INSERT INTO #TEmp
SELECT *
FROM table_a
INNER JOIN cte_a ON condition_a
IF NOT EXISTS(SELECT 1 FROM #TEmp)
BEGIN;
INSERT INTO #TEmp
SELECT *
FROM table_a
WHERE NOT EXISTS (
SELECT *
FROM table_b
INNER JOIN cte_b ON condition_b
)

This is the nature of CTE's.
The best thing would be to not use a CTE and use a join, but I assume you can't.
I'd suggest you try using a sub-query so the engine can process it all at once.

I would phrase this using a union, and then excluding the second half of the union on cte_b should the first CTE have any results.
;WITH cte_a AS (
...
),
cte_b AS (
...
)
SELECT *
FROM table_a
INNER JOIN cte_a ON condition_a
UNION ALL
SELECT *
FROM table_b
INNER JOIN cte_b ON condition_b
WHERE NOT EXISTS (
SELECT 1
FROM table_a
INNER JOIN cte_a ON condition_a
)

;WITH cte1 AS
(
....
),
cte2 AS
(
...
)
SELECT *
FROM table_a
INNER JOIN cte_a ON condition_a ---Choose Your Condition...
UNION ALL
SELECT *
FROM table_b
INNER JOIN cte_b ON condition_b ---Choose Your Condtion....
WHERE NOT EXISTS (SELECT * FROM cte2)
Note:-
A CTE must be followed by a single SELECT, INSERT, UPDATE, or DELETE statement that references some or all the CTE columns.

Related

Snowflake using CTE to create table

Does Snowflake allow you to create a series of CTEs then join them together at the end to create a table?
For example:
with CTE1 as ( SELECT * FROM TABLE1)
,CTE2 AS (SELECT * FROM TABLE2)
,CTE3 AS (SELECT * FROM TABLE3)
CREATE TABLE TABLE_NAME_HERE AS
SELECT * FROM CTE1 AS 1
LEFT JOIN CTE2 AS 2 ON 1.KEY = 2.KEY
LEFT JOIN CTE3 AS 3 ON 1.KEY = 3.KEY
I'm getting a unexpected 'CREATE'. error
It's an interesting one, because that CTAS (CREATE TABLE AS) form is
CREATE TABLE <name> AS <select>
and SELECT form is can have a CTE and a CTE form is
WITH <name> AS <select>
CTE can also be in sub-select given "it's just a select", thus
SELECT <columns>
FROM (
WITH cte_1 AS (
SELECT <columns>
FROM table
)
SELECT <columns>
FROM cte_1
)
which shows why the form that Lukasz shows is correct because if we add more parens
CREATE TABLE name AS (
WITH cte_1 AS (
SELECT <columns>
FROM table
)
SELECT <columns>
FROM cte_1
)
Yes, it is possible:
CREATE TABLE TABLE_NAME_HERE AS
WITH CTE1 as ( SELECT * FROM TABLE1)
,CTE2 AS (SELECT * FROM TABLE2)
,CTE3 AS (SELECT * FROM TABLE3)
SELECT * -- here should be explicit column list to avoid name duplication error
FROM CTE1 AS 1
LEFT JOIN CTE2 AS 2 ON 1.KEY = 2.KEY
LEFT JOIN CTE3 AS 3 ON 1.KEY = 3.KEY;

Postgresql - Update CTE result from CTE Output?

I need to update CTE o/p one of the column value (top 1 record) based on the latest timestamp & then return.
Query
WITH cte AS (
select
dt_zone.zone_name,
dt_material_status.mtstatus_name,
dt_historicalzone.visit_time_in
FROM ((public.dt_historicalzone
INNER JOIN dt_material_status
ON dt_historicalzone.mtstatus_id = dt_material_status.mtstatus_id)
INNER JOIN dt_zone ON dt_historicalzone.zone_id = dt_zone.zone_id)
WHERE material_id = 'ELS46885'
ORDER BY dt_historicalzone.zone_id DESC)
UPDATE cte SET cte.mtstatus_name = true WHERE SELECT * FROM cte LIMIT 1;
SELECT * FROM cte
You may try using an update join, with Postgres' syntax, including a CTE for the limit portion of the query:
WITH cte AS (
SELECT dh.mtstatus_id
FROM dt_historicalzone dh
INNER JOIN dt_zone dz
ON dh.zone_id = dz.zone_id
ORDER BY zone_id DESC
LIMIT 1
)
UPDATE dt_material_status d
SET mtstatus_name = true
FROM cte t
WHERE d.mtstatus_id = t.mtstatus_id AND
d.material_id = 'ELS46885';
when you update a CTE, the background table is getting updated. You can have only one statement below CTE. Post that CTE loses its scope. You can only go for UPDATE statement, post CTE. I have modified the CTE and updated the top 1 row.
The above statement is applicable for SQL Server. In Postgres, the CTE cannot be target of UPDATE statements. See the below error in Postgres.
Query Error: error: relation "cte" cannot be the target of a modifying
statement
WITH cte AS (
select top 1
dt_zone.zone_name,
dt_material_status.mtstatus_name,
dt_historicalzone.visit_time_in
FROM public.dt_historicalzone
INNER JOIN dt_material_status
ON dt_historicalzone.mtstatus_id = dt_material_status.mtstatus_id
INNER JOIN dt_zone ON dt_historicalzone.zone_id = dt_zone.zone_id
WHERE material_id = 'ELS46885'
ORDER BY dt_historicalzone.zone_id DESC)
UPDATE cte
SET mtstatus_name = true
I have tried with sample data for a CTE update. Below works fine in SQL Server.
create table #test(a int)
create table #test2(a int, b int)
insert into #test values (1)
insert into #test2 values (1,1)
;WITH CTE as
(
select top 1 t.a, t2.b
FROM #test as t
join #test2 as t2
on t.a = t2.a
order by t.a desc
)
update cte set b = 0
select * from #test2

SQL IN clause multiple columns and multiple value

This query is fine works.
SELECT * FROM TABLE WHERE 330110042 IN (iItem01,iItem02,iItem03,iItem04,iItem05,iItem_1,iItem_2,iItem_3,iItem_4,iItem_5,iItem_6,iItem_7,iItem_8,iItem_9,iItem_10,iItem_11,iItem_12,iItem_13,iItem_14,iItem_15,iItem_16,iItem_17,iItem_18,iItem_19,iItem_20,iItem_21,iItem_22,iItem_23,iItem_24,iItem_25,iItem_26,iItem_27,iItem_28,iItem_29,iItem_30)
But this query didnt work.
SELECT * FROM TABLE WHERE 330110042, 330110002, 330110002 IN (iItem01,iItem02,iItem03,iItem04,iItem05,iItem_1,iItem_2,iItem_3,iItem_4,iItem_5,iItem_6,iItem_7,iItem_8,iItem_9,iItem_10,iItem_11,iItem_12,iItem_13,iItem_14,iItem_15,iItem_16,iItem_17,iItem_18,iItem_19,iItem_20,iItem_21,iItem_22,iItem_23,iItem_24,iItem_25,iItem_26,iItem_27,iItem_28,iItem_29,iItem_30)
How i work in SQL Server?
It's difficult to tell your exact goal here, but one possibility would be to turn the list of values into a table structure of its own. A Common Table Expression might work:
;WITH Ids AS
(
SELECT 330110042 AS Id
UNION ALL
SELECT 330110002
)
SELECT t.*
FROM [Table] t
INNER JOIN Ids i ON t.iItem01 = i.Id OR t.iItem02 = i.Id OR...
But, maybe a solution with UNPIVOT would be more elegant. I presume that your table has a primary key column called Id:
;WITH Unpivoted AS
(
SELECT Id, ColName, ColValue
FROM (SELECT Id, iItem01, iItem02, iItem03
FROM [Table] t) p
UNPIVOT
(ColValue FOR ColName IN (iItem01, iItem02, iItem03)) AS unpvt
)
SELECT t.*
FROM [Table] t
WHERE EXISTS (SELECT 1 FROM Unpivoted u
WHERE t.Id = u.Id
AND u.ColValue IN (330110042, 330110002))
Of course, you would add all the necessary columns. I added only the first three for this example.

SQL server Temp table with joins inside other select

I have the following structure:
Create #temp
Select ...inser...into #temp where ...
(select ... from #temp
Join tblA where ... )
UNION
(Select ... from #temp
join tblB where ... )
After build above table I need to be able to perform WHERE, JOINS, ...
Something like:
Select ... from (above statement)
join ....
where....
I don't know how of if a #temp,joins, union... can be inside other select.
OR only thing I can do is create a #Temp2 inserting with first statement result and then work with other join,where... ?
UPDATE 1:
I also trying:
With cte (query returned columns)
as
(same query I was using to build my #temp as before)
(select ... from cte
join tblA
where...)
UNION
(select ... from cte
join tblB
where...)
But Im stuck at same point in how to perform other joins, where... with above total result
Create #temp
Select ...inser...into #temp where ...
;with temp2 as
(
select ... from #temp Join tblA where ...
UNION
Select ... from #temp join tblB where ...
)
select ... from temp2
join ....
where....
Actually you can do it without temp-table:
WITH myCTE [ ( column_name [,...n] ) ]
AS
( here you define your query )
and after that you just do your Select but use CTE
Select ... from myCTE
join ....
where....
about CTE you can read Here
After Update
Select fields from myCTE join table1
Union
Select fields from myCTE join table2
Without brackets in your query

Skip in SQL Server subquery

I want to use left outer join like this:
SELECT ...
FROM Table1
LEFT OUTER JOIN
(SELECT only e.g. 3rd record... , SomeField FROM Table2) tbl2
ON Table1.SomeField = tbl2.SomeField
How can I do that, if I need the subquery to select not just the 3rd record from Table2, but the 3rd record among the Table2 records that have SomeField = Table1.SomeField?
Thanks.
If this is sql server 2005 or newer, you might use row_number():
LEFT JOIN
(
select *
from
(
select *,
row_number() over (order by something) rn
from Table2
where Table2.Column = Table1.Column
) a
where a.rn = 3
) a
Unfortunately you need to nest it a level deeper because you cannot use row_number in a condition directly.
EDIT:
My bad - i didn't really notice the join part. If you want to join derived table, use this:
LEFT JOIN
(
select *,
row_number() over (partition by SomeField order by something) rn
from Table2
) tbl2
ON Table1.SomeField = tbl2.SomeField
AND tbl2.rn = 3
Note: you need ORDER BY in row_number() to keep things consistent.

Resources