case - query optimization - tsql

I have this query :
INSERT INTO #t1 (VALUE1, VALUE2)
SELECT
CASE
WHEN EXISTS(SELECT * FROM T2)
THEN SELECT VALUE1, VALUE2 FROM T2
END
This does not work , I get : "The select list for the INSERT statement contains fewer items than the insert list. The number of SELECT values must match the number of INSERT columns."
Instead I have to use :
INSERT INTO #t1 (VALUE1, VALUE2)
SELECT
CASE
WHEN EXISTS(SELECT * FROM T2)
THEN SELECT VALUE1 FROM T2
END
CASE
WHEN EXISTS(SELECT * FROM T2)
THEN SELECT VALUE2 FROM T2
END
But this decreases performance. Is there a solution to do it properly? Within a single CASE, to benefit from a sinqle query for the second table t2.

EXISTS(SELECT * FROM T2) doesn't do anything and the syntax is wrong
Try this:
INSERT INTO #t1 (VALUE1, VALUE2)
SELECT VALUE1, VALUE2 FROM T2
This would be valid as well, although not very useful, but it seems it is what you are trying to do:
INSERT INTO #t1 (VALUE1, VALUE2)
SELECT VALUE1, VALUE2 FROM T2
WHERE exists (SELECT 1 FROM T2)

Why not:
INSERT INTO #t1 (VALUE1, VALUE2)
SELECT VALUE1, VALUE2 FROM T2
If there are no rows in T2 nothing will be inserted into #t1.

Just simplify:
INSERT INTO #t1 (VALUE1, VALUE2)
SELECT VALUE1, VALUE2
FROM T2
The case where there are no rows doesn't do anything here either. You don't need to do a special test for that.

Couldn't you use something like
INSERT INTO #t1 (VALUE1, VALUE2)
SELECT value1, value2 from #t2 WHERE NOT (value1 IS NULL AND value2 IS NULL)
instead?
Otherwise your intention might be
INSERT INTO #t1 (VALUE1)
SELECT CONCAT(value1, value2) from t2 Couldn't you use something like
INSERT INTO #t1 (VALUE1, VALUE2)
SELECT value1, value2 from t2 WHERE NOT (value1 IS NULL AND value2 IS NULL)
instead?
Otherwise your intention might be
INSERT INTO #t1 (VALUE1)
SELECT value1, value2 from #t2 WHERE NOT (value1 IS NULL AND value2 IS NULL)
which causes the 't2.value2' to be inserted in t1.value1 if t2.value2 is null

Related

Produce Separate Results For Multiple Values

I am attempting to produce multiple results for each value.
Postgres
Select count (*) from table_name
Where column_id IN(value1, value2, value3)
Looks like the outputs are adding everything together and I'd like the individual results for each value.
I was expecting the output for value 1, value 2, and value 3 not the combined sum, the individual values.
You'll want to use either a GROUP BY clause
SELECT column_id, count(*)
FROM table_name
WHERE column_id IN (value1, value2, value3)
GROUP BY column_id
or run multiple separate counts
SELECT
count(*) FILTER (WHERE column_id = value1) AS value1_count,
count(*) FILTER (WHERE column_id = value2) AS value2_count,
count(*) FILTER (WHERE column_id = value3) AS value3_count
FROM table_name

Delete duplicate rows with different values in columns

I didn't find my case on the Internet. Tell me how i can delete duplicates if the values are in different columns.
I have a table with a lot of values, for example:
|Id1|Id2|
|89417980|89417978|
|89417980|89417979|
|89417978|89417980|
|89417979|89417980|
I need to exclude duplicates and leave in the answer only:
|Id1|Id2|
|89417980|89417978|
|89417980|89417979|
min/max does not work here, as the values may be different.
I tried to union/join tables on a table/exclude results with temporary tables, but in the end I come to the beginning.
Assuming id1 and id2 are primary keys columns you could try this
DECLARE #tbl table (id1 int, id2 int )
INSERT INTO #tbl
SELECT 89417980, 89417978
UNION SELECT 89417980, 89417979
UNION SELECT 89417978, 89417980
UNION SELECT 89417979, 89417980
SELECT * FROM #tbl
;WITH CTE AS (--Get comparable value as "cs"
SELECT
IIF(id1 > id2, CHECKSUM(id1, id2), CHECKSUM(id2,id1)) as cs
, id1
, id2
, ROW_NUMBER() OVER (order by id1, id2) as rn
FROM #tbl
)
, CTE2 AS ( --Get rows to keep
SELECT MAX (rn) as rn
FROM CTE
GROUP BY cs
HAVING COUNT(*) > 1
)
DELETE tbl -- Delete all except the rows to keep
FROM #tbl tbl
WHERE NOT EXISTS(SELECT 1
FROM CTE2
JOIN CTE ON CTE.rn = CTE2.rn
WHERE CTE.id1 = tbl.id1
AND CTE.id2 = tbl.id2
)
SELECT * FROM #tbl

sql recursion: find tree given middle node

I need to get a tree of related nodes given a certain node, but not necessary top node. I've got a solution using two CTEs, since I am struggling to squeeze it all into one CTE :). Might somebody have a sleek solution to avoid using two CTEs? Here is some code that I was playing with:
DECLARE #temp AS TABLE (ID INT, ParentID INT)
INSERT INTO #temp
SELECT 1 ID, NULL AS ParentID
UNION ALL
SELECT 2, 1
UNION ALL
SELECT 3, 2
UNION ALL
SELECT 4, 3
UNION ALL
SELECT 5, 4
UNION ALL
SELECT 6, NULL
UNION ALL
SELECT 7, 6
UNION ALL
SELECT 8, 7
DECLARE #startNode INT = 4
;WITH TheTree (ID,ParentID)
AS (
SELECT ID, ParentID
FROM #temp
WHERE ID = #startNode
UNION ALL
SELECT t.id, t.ParentID
FROM #temp t
JOIN TheTree tr ON t.ParentID = tr.ID
)
SELECT * FROM TheTree
;WITH Up(ID,ParentID)
AS (
SELECT t.id, t.ParentID
FROM #temp t
WHERE t.ID = #startNode
UNION ALL
SELECT t.id, t.ParentID
FROM #temp t
JOIN Up c ON t.id = c.ParentID
)
--SELECT * FROM Up
,TheTree (ID,ParentID)
AS (
SELECT ID, ParentID
FROM Up
WHERE ParentID is null
UNION ALL
SELECT t.id, t.ParentID
FROM #temp t
JOIN TheTree tr ON t.ParentID = tr.ID
)
SELECT * FROM TheTree
thanks
Meh. This avoids using two CTEs, but the result is a brute force kludge that hardly qualifies as "sleek" as it won’t be efficient if your table is at all sizeable. It will:
Recursively build all possible hierarchies
As you build them, flag the target NodeId as you find it
Return only the targeted tree
I threw in column “TreeNumber” on the off-chance the TargetId appears in multiple hierarchies, or if you’d ever have multiple values to check in one pass. “Depth” was added to make the output a bit more legible.
A more complex solution like #John’s might do, and more and subtler tricks could be done with more detailed table sturctures.
DECLARE #startNode INT = 4
;WITH cteAllTrees (TreeNumber, Depth, ID, ParentID, ContainsTarget)
AS (
SELECT
row_number() over (order by ID) TreeNumber
,1
,ID
,ParentID
,case
when ID = #startNode then 1
else 0
end ContainsTarget
FROM #temp
WHERE ParentId is null
UNION ALL
SELECT
tr.TreeNumber
,tr.Depth + 1
,t.id
,t.ParentID
,case
when tr.ContainsTarget = 1 then 1
when t.ID = #startNode then 1
else 0
end ContainsTarget
FROM #temp t
INNER JOIN cteAllTrees tr
ON t.ParentID = tr.ID
)
SELECT
TreeNumber
,Depth
,ID
,ParentId
from cteAllTrees
where TreeNumber in (select TreeNumber from cteAllTrees where ContainsTarget = 1)
order by
TreeNumber
,Depth
,ID
Here is a technique where you can select the entire hierarchy, a specific node with all its children, and even a filtered list and how they roll.
Note: See the comments next to the DECLAREs
Declare #YourTable table (id int,pt int,name varchar(50))
Insert into #YourTable values
(1,null,'1'),(2,1,'2'),(3,1,'3'),(4,2,'4'),(5,2,'5'),(6,3,'6'),(7,null,'7'),(8,7,'8')
Declare #Top int = null --<< Sets top of Hier Try 2
Declare #Nest varchar(25) = '|-----' --<< Optional: Added for readability
Declare #Filter varchar(25) = '' --<< Empty for All or try 4,6
;with cteP as (
Select Seq = cast(1000+Row_Number() over (Order by name) as varchar(500))
,ID
,pt
,Lvl=1
,name
From #YourTable
Where IsNull(#Top,-1) = case when #Top is null then isnull(pt,-1) else ID end
Union All
Select Seq = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.name)) as varchar(500))
,r.ID
,r.pt
,p.Lvl+1
,r.name
From #YourTable r
Join cteP p on r.pt = p.ID)
,cteR1 as (Select *,R1=Row_Number() over (Order By Seq) From cteP)
,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select Distinct
A.R1
,B.R2
,A.ID
,A.pt
,A.Lvl
,name = Replicate(#Nest,A.Lvl-1) + A.name
From cteR1 A
Join cteR2 B on A.ID=B.ID
Join (Select R1 From cteR1 where IIF(#Filter='',1,0)+CharIndex(concat(',',ID,','),concat(',',#Filter+','))>0) F on F.R1 between A.R1 and B.R2
Order By A.R1

How to generate RowID in Sql server 2000 without using identity column

Let me frame my question ....
I have say
Name
A
B
C
A
D
B
What I want is
ID Name
1 A
2 B
3 C
4 A
5 D
6 B
If I write
SELECT name, (SELECT COUNT(*) FROM #t AS i2 WHERE i2.Name <= i1.Name) As rn FROM #t AS i1
it will work fine if all the names are distinct/unique...What if they are not(as in this example)
Even NEWID() does not make the trick as it varies overtime?
I am using sql server 2000...
Please help
Here are 2 ways of solving it
1.
DECLARE #t TABLE ([ID] [int] IDENTITY(1,1), name CHAR)
INSERT #t VALUES ('b')
INSERT #t VALUES ('a')
INSERT #t VALUES ('c')
INSERT #t VALUES ('b')
SELECT * FROM #t
2.
DECLARE #t2 TABLE (name CHAR)
INSERT #t2 (name) VALUES ('b')
INSERT #t2 (name) VALUES ('a')
INSERT #t2 (name) VALUES ('c')
INSERT #t2 (name) VALUES ('b')
SELECT ID = ROW_NUMBER() OVER (ORDER BY b), name
FROM (SELECT name, null b FROM #t2) temp

Aggregate GREATEST in T-SQL

My SQL is rusty -- I have a simple requirement to calculate the sum of the greater of two column values:
CREATE TABLE [dbo].[Test]
(
column1 int NOT NULL,
column2 int NOT NULL
);
insert into Test (column1, column2) values (2,3)
insert into Test (column1, column2) values (6,3)
insert into Test (column1, column2) values (4,6)
insert into Test (column1, column2) values (9,1)
insert into Test (column1, column2) values (5,8)
In the absence of the GREATEST function in SQL Server, I can get the larger of the two columns with this:
select column1, column2, (select max(c)
from (select column1 as c
union all
select column2) as cs) Greatest
from test
And I was hoping that I could simply sum them thus:
select sum((select max(c)
from (select column1 as c
union all
select column2) as cs))
from test
But no dice:
Msg 130, Level 15, State 1, Line 7
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
Is this possible in T-SQL without resorting to a procedure/temp table?
UPDATE: Eran, thanks - I used this approach. My final expression is a little more complicated, however, and I'm wondering about performance in this case:
SUM(CASE WHEN ABS(column1 * column2) > ABS(column3 * column4)
THEN column5 * ABS(column1 * column2) * column6
ELSE column5 * ABS(column3 * column4) * column6 END)
Try this:
SELECT SUM(CASE WHEN column1 > column2
THEN column1
ELSE column2 END)
FROM test
Try this... Its not the best performing option, but should work.
SELECT
'LargerValue' = CASE
WHEN SUM(c1) >= SUM(c2) THEN SUM(c1)
ELSE SUM(c2)
END
FROM Test
SELECT
SUM(MaximumValue)
FROM (
SELECT
CASE WHEN column1 > column2
THEN
column1
ELSE
column2
END AS MaximumValue
FROM
Test
) A
FYI, the more complicated case should be fine, so long as all of those columns are part of the same table. It's still looking up the same number of rows, so performance should be very similar to the simpler case (as SQL Server performance is usually IO bound).
How to find max from single row data
-- eg (empid , data1,data2,data3 )
select emplid , max(tmp.a)
from
(select emplid,date1 from table
union
select emplid,date2 from table
union
select emplid,date3 from table
) tmp , table
where tmp.emplid = table.emplid
select sum(id) from (
select (select max(c)
from (select column1 as c
union all
select column2) as cs) id
from test
)
The best answer to this is simply put :
;With Greatest_CTE As
(
Select ( Select Max(ValueField) From ( Values (column1), (column2) ) ValueTable(ValueField) ) Greatest
From Test
)
Select Sum(Greatest)
From Greatest_CTE
It scales a lot better than the other answers with more than two value columns.