Run UNION SELECT only if the fist select is not empty - sql

I have the following stored procedure that returns rows for an endless scroll page. How can I check that newTable is empty? If it's empty then I don't want the UNION SELECT to be executed. Note that newTable is a very complex query so I don't want to execute more than once.
#offset INT,
#fetch INT
WITH newTable AS
(
SELECT * FROM table1
ORDER BY id OFFSET #offset ROWS FETCH NEXT #fetch ROWS ONLY
)
Don't run the following if newTable has reached the last row and is empty
SELECT * FROM newTable
UNION
SELECT * FROM table3
UNION
SELECT * FROM table4
ORDER BY id

You can probably do something like this:
DECLARE #offset INT,
#fetch INT;
WITH newTable AS
(
SELECT * FROM table1
ORDER BY id OFFSET #offset ROWS FETCH NEXT #fetch ROWS ONLY
)
SELECT *
FROM newTable
UNION
SELECT *
FROM table3
WHERE EXISTS (SELECT 1 FROM newTable)
UNION
SELECT *
FROM table4
WHERE EXISTS (SELECT 1 FROM newTable)
ORDER BY id
I'm pretty sure it will not execute the cte again. If it does, you can use a temporary table instead of a cte:
SELECT <ColumnsList> INTO #TemporaryTable
FROM....
and then your UNION query.
SELECT *
FROM #TemporaryTable
UNION
SELECT *
FROM table3
WHERE EXISTS (SELECT 1 FROM #TemporaryTable)
UNION
SELECT *
FROM table4
WHERE EXISTS (SELECT 1 FROM #TemporaryTable)
ORDER BY id

Related

Merge three tables in Select query by rule 3, 2, 1 records from each table

Merge three tables in a Select query by rule 3, 2, 1 records from each table as follows:
TableA: ID, FieldA, FieldB, FieldC,....
TableB: ID, FieldA, FieldB, FieldC,....
TableC: ID, FieldA, FieldB, FieldC,....
ID : auto number in each table
FieldA will be unique in all three tables.
I am looking for a Select query to merge three tables as follows:
TOP three records from TableA sorted by ID
TOP two records from TableB sorted by ID
TOP 1 record from TableC sorted by ID
Repeat this until select all records from all three tables.
If some table has fewer records or does not meet the criteria, ignore that and continue with others.
My attempt:
I did it totally through programming way, like cursors and If conditions inside a SQL Server stored procedure.
It makes delay.
This requires a formula that takes row numbers from each table and transforms it into a series of integers that skips the desired values.
In the query below, I am adding some CTE for the sake of shortening the formula. The real magic is in the UNION. Also, I am adding an additional field for your control. Feel free to get rid of it.
WITH A_Aux as (
SELECT 'A' As FromTable, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum, TableA.*
FROM TableA
), B_Aux AS (
SELECT 'B' As FromTable, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum, TableB.*
FROM TableB
), C_Aux AS (
SELECT 'C' As FromTable, ROW_NUMBER() OVER (Order BY ID) AS RowNum, TableC.*
FROM TableC
)
SELECT *
FROM (
SELECT RowNum+3*FLOOR((RowNum-1)/3) As ColumnForOrder, A_Aux.* FROM A_Aux
UNION ALL
SELECT 3+RowNum+4*FLOOR((RowNum-1)/2), B_Aux.* FROM B_Aux
UNION ALL
SELECT 6*RowNum, C_Aux.* FROM C_Aux
) T
ORDER BY ColumnForOrder
PS: note the pattern Offset + RowNum + (6-N) * Floor((RowNum-1)/N) to group N records together (it of course simplifies a lot for TableC).
PPS: I don't have a SQL server at hand to test it. Let me know if there is a syntax error.
You may try this..
GO
select * into #temp1 from (select * from table1) as t1
select * into #temp2 from (select * from table2) as t2
select * into #temp3 from (select * from table3) as t3
select * into #final from (select col1, col2, col3 from #temp1 where 1=0) as tb
declare #i int
set #i=1
while( (select COUNT(*) from #temp1)>#i)
Begin
;with ct1 as (
select ROW_NUMBER() over (order by id) as Slno, * from #temp1
),ct2 as (
select ROW_NUMBER() over (order by id) as Slno, * from #temp2
),ct3 as (
select ROW_NUMBER() over (order by id) as Slno, * from #temp3
),cfinal as (
select top 3 * from #temp1
union all
select top 2 * from #temp2
union all
select top 1 * from #temp3
)
insert into #final ( col1 , col2, col3 )
select col1, col2, col3 from cfinal
delete from #temp1 where id in (select top 3 ID from #temp1)
delete from #temp2 where id in (select top 2 ID from #temp2)
delete from #temp3 where id in (select top 1 ID from #temp3)
set #i = #i+1
End
Select * from #final
Drop table #temp1
Drop table #temp2
Drop table #temp3
GO
First create temp table for all 3 tables with each insert delete the inserted record and this will result you the desired result, if nothing is missing from my side.
Please see to this if this works.
There is not a lot of information to go with here, but I assume you can use UNION to combine multiple statements.
SELECT * TableA ORDER BY ID DESC OFFSET 3 ROWS
UNION
SELECT * TableB ORDER BY ID DESC OFFSET 2 ROWS
UNION
SELECT * TableC ORDER BY ID DESC OFFSET 1 ROWS
Execute and see if this works.
/AF
From my understanding, I create three temp tables as ta, tb, tc.
select * into #ta from (
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
union all
select 'A' a
) a
select * into #tb from (
select 'B' b
union all
select 'B'
union all
select 'B'
union all
select 'B'
union all
select 'B'
) b
select * into #tc from (
select 'C' c
union all
select 'C'
union all
select 'C'
union all
select 'C'
union all
select 'C'
) c
If tables match you tables, then the output looks like A,A,A,B,B,C,A,A,A,B,B,C,A,B,C,C,C
T-SQL
declare #TAC int = (select count (*) from #ta) -- Table A Count = 7
declare #TBC int = (select count (*) from #tb) -- Table B Count = 5
declare #TAR int = #TAC % 3 -- Table A Reminder = 1
declare #TBR int = #TBC % 2 -- Table B Reminder = 1
declare #TAQ int = (#TAC - #TAR) / 3 -- Table A Quotient = (7 - 1) / 3 = 2, is will passed on NTILE
-- So we gonna split as two group (111), (222)
declare #TBQ int = (#TBC - #TBR) / 2 -- Table B Quotient = (5 - 1) / 2 = 2, is will passed on NTILE
-- So we gonna split as two group (11), (22)
select * from (
select *, NTILE (#TAQ) over ( order by a) FirstOrder, 1 SecondOrder from (
select top (#TAC - #TAR) * from #ta order by a
) ta -- 6 rows are obtained out of 7.
union all
select *, #TAQ + 1, 1 from (
select top (#TAR) * from #ta order by a desc
) ta -- Remaining one row is obtained. Order by desc is must
-- Here FirstOrder is next value of previous value.
union all
select *, NTILE (#TBQ) over ( order by b), 2 from (
select top (#TBC - #TBR) * from #tb order by b
) tb
union all
select *, #TBQ + 1, 2 from (
select top (#TBR) * from #tb order by b desc
) tb
union all
select *, ROW_NUMBER () over (order by c), 3 from #tc
) abc order by FirstOrder, SecondOrder
Let me explain the T-SQL:
Before that, FYR: NTILE and Row Number
Get the count.
Find the Quotient which will pass to NTILE function.
Order by the NTILE value and static.
Note:
I am using SQL Server 2017.
If T-SQL works fine, then you need to change the column in order by <yourcolumn>.

How to join multiple CTE's into a temp table

I have been given a script to clean up which uses approx 85 temp tables, I have been advised to use Common Table Expressions.
I have 3 CTE's, the first is the result of 7 tables pulled together using Union all. Followed by 2 more CTE's. The script runs up to:
select * from CTE_1
Union all
select * from CTE_2
Union all
select * from CTE_3
I then want to put all these results into a reusable table so I can then add some joins with various case statement logic. How can I put these into a temp table so that I can reference it later.
I'm looking to reduce the amount of temp tables so rather than put each CTE into a temp table I would ideally put multiple CTE's into one temp table. I currently have:
; with [CTE One] as (
select 1 as col
),
[CTE Two] as (
select 2 as col
),
[CTE Three] as (
select 3 as col
)
select * from CTE_1
Union all
select * from CTE_2
Union all
select * from CTE_3
Alternatively..
IF ( OBJECT_ID('tempdb..#temptable') IS NOT NULL )
BEGIN
DROP TABLE #temptable
END
CREATE TABLE #temptable
(
val int
)
;
WITH [CTE One]
AS ( SELECT 1 AS col
),
[CTE Two]
AS ( SELECT 2 AS col
),
[CTE Three]
AS ( SELECT 3 AS col
)
INSERT INTO #temptable (val)
SELECT *
FROM ( SELECT *
FROM CTE_1
UNION ALL
SELECT *
FROM CTE_2
UNION ALL
SELECT *
FROM CTE_3
) T
Can't you just use into?
select *
into #temptable
from CTE_1
Union all
select * from CTE_2
Union all
select * from CTE_3;
I might also be inclined to use a table variable, if the code is structured appropriately.

How to avoid Sorting in Union ALL

MY question is simple, How do you avoid the automatic sorting which the UNION ALL query does?
This is my query
SELECT * INTO #TEMP1 FROM Final
SELECT * INTO #TEMP2 FROM #TEMP1 WHERE MomentId = #MomentId
SELECT * INTO #TEMP3 FROM #TEMP1 WHERE RowNum BETWEEN #StartRow AND #EndRow
SELECT * INTO #TEMP4 FROM (SELECT *FROM #TEMP3 UNION ALL SELECT *FROM #TEMP2) as tmp
SELECT DISTINCT * FROM #TEMP4
I'm using SQL Server 2008. I need the Union ALL to perform like a simple Concatenate, which it isn't! Appreciate your help in this.
I think you're mistaken on which operation is actually causing the sort. Check the code below, UNION ALL will not cause a sort. You may be looking at the DISTINCT operation, which uses a sort (it sorts all items and the eliminates duplicates)
CREATE TABLE #Temp1
(
i int
)
CREATE TABLE #temp2
(
i int
)
INSERT INTO #Temp1
SELECT 3 UNION ALL
SELECT 1 UNION ALL
SELECT 8 UNION ALL
SELECT 2
INSERT INTO #Temp2
SELECT 7 UNION ALL
SELECT 1 UNION ALL
SELECT 5 UNION ALL
SELECT 6
SELECT * INTO #TEMP3
FROM (SELECT * FROM #Temp1 UNION ALL SELECT * FROM #temp2) X
UNION ALL adds all the records where as UNION adds only new/distinct records.
Since you are using UNION ALL and using DISTINCT soon after, I think you are looking for UNION
SELECT * INTO #TEMP4 FROM
(
SELECT * FROM #TEMP3
UNION --JUST UNION
SELECT * FROM #TEMP2
) AnotherTemp
Or you can simplify it as
SELECT * INTO #TEMP4 FROM
SELECT DISTINCT *
FROM Final
WHERE MomentId = #MomentId OR RowNum BETWEEN #StartRow AND #EndRow
I'm not familiar with SQL-Server, but you might get my idea
select *, 'A' tid, rownumber() tno from tableA
union all
select *, 'B', rownumber() from tableB
order by tid, tno;
This should get you all records of tableA in their specific order, followed by all records of tableB in their specific order.

Set criteria to be used in multiple SQL select queries

Need to set criteria for running multiple queries, but only want to change once. For example, want to set year, period, document so that...
select * from tbl1 where tbl1.yr = year and ...
select * from tbl2 where tbl2.yr = year, and ...
Create a view:
CREATE VIEW yourview AS
SELECT * from tbl1
UNION ALL
SELECT * from tbl2
Then query it:
SELECT * FROM yourview
WHERE tbl1.yr = year AND ...
You may also want to know from which table each row comes. This can be achieved by adding an extra column to the view:
CREATE VIEW yourview AS
SELECT 'tbl1' AS src, * from tbl1
UNION ALL
SELECT 'tbl2' AS src, * from tbl2
If they're truly different queries, not related, you may have to resort to Dynamic SQL, e.g.
DECLARE #sCondition VARCHAR(50)
SET #sCondition = 'yr = 2012'
DECLARE #sQuery1 VARCHAR(1000)
SET #sQuery1 = 'select * from tbl1 where ' + #sCondition
-- DECLARE other queries in similar faction OR combine multiple queries into single variable
EXEC (#sQuery1)
With CTE; Note: you should have same number of columns and matching datatypes from both tables as you are doing a blind union of select *
;with cte (myYear)
as (
select #year as myYear
)
select * from table1 t1 where t1.year in (select myYear from cte)
union all
select * from table2 t2 where t2.year in (select myYear from cte)

sql conditional union on rowcount

I have an SQL function which returns a list of Teams.
I want to join an additional list to that list with a union, but only if first select returns more than one rows.
Something like:
CREATE FUNCTION Teams()
RETURNS TABLE
AS
RETURN
(
SELECT * FROM TABLE1
if ##rowcount>1
UNION
SELECT * FROM TABLE2
end if
)
Not pretty but this should work:
CREATE FUNCTION Teams()
RETURNS TABLE
AS
RETURN
(
SELECT * FROM TABLE1
UNION
SELECT * FROM TABLE2 WHERE EXISTS (SELECT * FROM TABLE1)
)
If the select from the first table is complicated you could put it into a CTE:
CREATE FUNCTION Teams()
RETURNS TABLE
AS
RETURN
(
WITH Result AS
(
SELECT * FROM TABLE1 WHERE ComplicatedConditions = 1
)
SELECT * FROM Result
UNION
SELECT * FROM TABLE2 WHERE EXISTS (SELECT * FROM Result)
)
If I understand correctly
CREATE FUNCTION Teams()
RETURNS TABLE
AS
RETURN
(
if (select COUNT(*) from TABLE2)>1
SELECT * FROM TABLE1
UNION
SELECT * FROM TABLE2
else
SELECT * FROM TABLE1
)
One way would be to do:
IF EXISTS(SELECT 1 FROM TABLE1)
SELECT * FROM TABLE1
UNION
SELECT * FROM TABLE2
ELSE
SELECT * FROM TABLE1