Updating a column in a temp table with new values - sql

I have the following temp table structure:
CREATE TABLE #TempTable (
ID INT,
CId TINYINT,
TagId INT,
Beginning_X DECIMAL(18,5),
Beginning_Y DECIMAL(18,5),
Ending_X DECIMAL(18,5),
Ending_Y DECIMAL(18,5))
INSERT INTO #TempTable (ID, CId, TagId, Beginning_X, Beginning_Y)
SELECT ID, CId,TagId, X, Y
FROM Table_1
WHERE IsRepeat = 1
INSERT INTO #TempTable(Ending_X, Ending_Y)
SELECT X,Y
FROM Table_1 t
WHERE t.ID IN (SELECT ID+1
FROM Table_1
where IsRepeat = 1))
The second insert removes all the values from the first insert statement and I can't figure out why. I want to append the the results from the second insert to the first insert and have one solid table.
EDIT: I think I found the solution:
UPDATE t
SET t.Ending_X = p.X, t.Ending_Y = p.Y
FROM #TempTable t, Table_1 p
WHERE p.ID IN (SELECT ID+1 FROM Table_1 where IsRepeat = 1)
AND p.ID-1 = t.ID

I think you need this
UPDATE t
SET Ending_X = X, Ending_Y = Y
FROM #TempTable t
WHERE t.ID IN (SELECT ID+1 FROM Table_1 where IsRepeat = 1))

How about unioning the two queries
INSERT INTO #TempTable (ID, CId, TagId, Beginning_X, Beginning_Y)
SELECT ID, CId,TagId, X, Y
from Table_1
WHERE IsRepeat = 1
UNION
SELECT Convert(Null, int) AS ID, Convert(Null, tinyint) AS CId,
Convert(Null, int) AS TagId, X,Y
FROM Table_1 t
WHERE t.ID IN (SELECT ID+1 FROM Table_1 where IsRepeat = 1))

Related

SQL Server script for SQLite

I need to convert scripts for SQL Server, so I can use them for SQLite.
The scripts look like this:
DECLARE #var1 int = (SELECT Id FROM Table1 WHERE id = 'val1')
DECLARE #var2 int = (SELECT Id FROM Table2 WHERE id = 'val2')
IF NOT EXISTS(SELECT * FROM Table3 WHERE id_1 = #var1 and id_2 = #var2)
INSERT INTO Table3(id_1, id_2) VALUES (#var1, #var2)
GO
IF NOT EXISTS(SELECT * FROM Table4 WHERE id ='val3')
INSERT INTO Table4(id, desc) values ('111', 'new entry')
GO
What is the best practice for executing these scripts in SQLite?
SQLite does not support variables.
You can use a CTE that returns the values of the 2 variables as columns:
WITH cte(var1, var2) AS (
SELECT (SELECT Id FROM Table1 WHERE id = 'val1'), (SELECT Id FROM Table2 WHERE id = 'val2')
)
INSERT INTO Table3(id_1, id_2)
SELECT var1, var2
FROM cte
WHERE NOT EXISTS (SELECT * FROM Table3 t3 INNER JOIN cte c ON t3.id_1 = c.var1 AND t3.id_2 = c.var2);
INSERT INTO Table4(id, `desc`)
SELECT '111', 'new entry'
WHERE NOT EXISTS (SELECT * FROM Table4 WHERE id ='val3')

Need to return an ID which has start and END in sql server

I have a scenario wherein I need to find the ID which only has start and END in it. Below is the table for reference.
Declare #T Table ( ID int, Name varchar(100))
Insert into #T values (1,'Start')
Insert into #T values (1,'END')
Insert into #T values (1,'Stuart')
Insert into #T values (1,'robin')
Insert into #T values (2,'Start')
Insert into #T values (2,'END')
Insert into #T values (3,'Start')
Insert into #T values (4,'END')
I want the Output as:
ID Name
2 Start
2 END
I want those ID which only has start and end in it.
What I tried so far:
SELECT * FROM #T t
WHERE EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'start')
AND EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'END')
But my query is giving ID 1 as well.
Can someone please help me rectify the problem.
I presume your issue is that record 1 has a 'Stuart' in it too?
As such, you can do a similar check in the WHERE e.g.,
SELECT * FROM #T t
WHERE EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'start')
AND EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'END')
AND NOT EXISTS (SELECT * FROM #T WHERE id = t.id AND name NOT IN ('start','END'))
Note that you may want to consider
What happens if you have two 'start' rows or two 'end' rows (e.g., start-start-end)? Can you even have two 'start' rows (e.g., start-start)?
What happens if you have a blank/NULL (e.g., start-NULL-end)?
EDIT: removed 'What happens if they're out of order (e.g., end-start)?' as a question as there is no sorting in the data at all (e.g., not even an implicit sort).
You can go for CTE to get group wise count and total count as 2.
Declare #T Table ( ID int, Name varchar(100))
Insert into #T values (1,'Start')
Insert into #T values (1,'END')
Insert into #T values (1,'Stuart')
Insert into #T values (1,'robin')
Insert into #T values (2,'Start')
Insert into #T values (2,'END')
Insert into #T values (3,'Start')
Insert into #T values (4,'END')
;WITH CTE_Total_StartEnd AS
(
select id, count(*) AS Total_Cnt
, COUNT( case when Name IN ('Start') THEN 1 END) as start_cnt
, COUNT( case when Name IN ('End') THEN 1 END) as end_cnt
from #t
group by id
having COUNT( case when Name IN ('Start') THEN 1 END) =1 and
COUNT( case when Name IN ('End') THEN 1 END) = 1 and
count(*) = 2
)
SELECT t.* from #t t
inner join CTE_Total_StartEnd as c
ON c.id = t.id
+----+-------+
| ID | Name |
+----+-------+
| 2 | Start |
| 2 | END |
+----+-------+
You can do this by using group by function also like below
WITH cte AS
(
SELECT 1 AS id , 'Start' AS name
UNION ALL
SELECT 1 AS id ,'END' AS name
UNION ALL
SELECT 1 AS id ,'Stuart' AS name
UNION ALL
SELECT 1 AS id ,'robin' AS name
UNION ALL
SELECT 2 AS id ,'Start' AS name
UNION ALL
SELECT 2 AS id ,'END' AS name
UNION ALL
SELECT 3 AS id ,'Start' AS name
UNION ALL
SELECT 4 AS id ,'END' AS name
)
SELECT T.ID,SUM(T.VAL)AS SUM
FROM
(
SELECT id,name , CASE WHEN name='Start' THEN 1
WHEN name='END' THEN 2
ELSE 3
END AS VAL
FROM cte
)T
GROUP BY T.ID
HAVING SUM(T.VAL) =3
could you please try this? Pls note i added collate command in the end of sql.
SQL Server check case-sensitivity?
SELECT * FROM #T t
WHERE EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'start' COLLATE SQL_Latin1_General_CP1_CS_AS)
AND EXISTS (SELECT * FROM #T WHERE id = t.id AND name = 'END' COLLATE SQL_Latin1_General_CP1_CS_AS)

SQL to display pivoted data

I have the following table:
and i want the following output displayed:
the above is basically the (quan*cst) for each day. Now I can acieve this by the following sql:
select t1.pid, isnull(b.m1,0) as day1sale, isnull(a.m2,0) as day2sale
from dbo.test1 t1
left join(select pid, sum(quan*cst) m1
from dbo.test1 where date='2017-05-01' group by pid) b on b.pid=t1.pid
left join (select pid, sum(quan*cst) m2
from dbo.test1 where date='2017-05-02' group by pid) a on a.pid=t1.pid
group by t1.pid,m2 ,m1
order by t1.pid
But i was wondering if there is a simpler way to do it without actually having to hard code the dates?
thanks in advance for the help!
Using a self join to get a list of distinct dates, the result set can be pivoted and the desired aggregates applied to each PID.
CREATE TABLE #TEMP_EXAMPLE
(
[DATE] DATE,
[ID] INT,
[PID] INT,
[QUAN] INT,
[CST] INT
)
INSERT INTO #TEMP_EXAMPLE VALUES('05/01/2017','1','1','2','3')
INSERT INTO #TEMP_EXAMPLE VALUES('05/01/2017','2','2','6','2')
INSERT INTO #TEMP_EXAMPLE VALUES('05/01/2017','3','3','5','1')
INSERT INTO #TEMP_EXAMPLE VALUES('05/01/2017','4','1','1','3')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','5','3','3','1')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','6','4','4','7')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','7','1','7','3')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','8','5','2','8')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','9','6','5','6')
INSERT INTO #TEMP_EXAMPLE VALUES('05/02/2017','10','2','8','2')
SELECT ROW_NUMBER() OVER(PARTITION BY 1 ORDER BY [DATE]) AS ID,*
INTO #TEMP_DYNAMIC_DATES
FROM (SELECT DISTINCT [DATE] FROM #TEMP_EXAMPLE ) AS X
SELECT * FROM #TEMP_DYNAMIC_DATES
SELECT PID,ISNULL(DAY1SALE,0) AS DAY1SALE,ISNULL(DAY2SALE,0) AS DAY2SALE FROM (
SELECT PID,SUM([QUAN] * [CST]) AS X, INDIC FROM (
SELECT A.*,
CASE WHEN B.ID = 1 THEN 'DAY1SALE'
WHEN B.ID = 2 THEN 'DAY2SALE'
END AS INDIC
FROM #TEMP_EXAMPLE AS A
JOIN #TEMP_DYNAMIC_DATES AS B
ON A.DATE = B.DATE
) AS X
GROUP BY PID,INDIC) AS O
PIVOT(SUM(X) FOR INDIC IN([DAY1SALE],[DAY2SALE])) AS PT

SQL UPDATE statement to switch two values in two rows

I'm using SQL Server to swap two values in two rows. Let me show:
[ord] [name]
1 John
4 Jack
7 Pete
9 Steve
11 Mary
Say, I need to swap [ord] numbers for "Pete" and "Steve" to make this table to be like so:
[ord] [name]
1 John
4 Jack
9 Pete
7 Steve
11 Mary
This seems like a trivial task but I can't seem to write an SQL UPDATE statement for it.
If 'Peter' and 'Steve' are unique in your table, this will do:
UPDATE TableX
SET ord = ( SELECT MIN(ord) + MAX(ord)
FROM TableX
WHERE name IN ('Peter', 'Steve')
) - ord
WHERE name IN ('Peter', 'Steve')
or (improved by #Erwin):
UPDATE TableX
SET ord = ( SELECT SUM(ord)
FROM TableX
WHERE name IN ('Peter', 'Steve')
) - ord
WHERE name IN ('Peter', 'Steve')
Use a CASE expression:
UPDATE yourtable
SET [ord] = CASE [ord] WHEN 9 THEN 7
WHEN 7 THEN 9 END
WHERE [ord] IN (7, 9)
This is very similar to your earlier question: SQL to move rows up or down in two-table arrangement
I prepared another demo on data.stackexchange.com for you.
Edit: the setup is simplified now, so I simplified my query accordingly.
WITH x AS (SELECT name, ord FROM t WHERE name = 'Pete') -- must be unique!
, y AS (SELECT name, ord FROM t WHERE name = 'Steve') -- must be unique!
UPDATE t
SET ord = z.ord
FROM (
SELECT x.name, y.ord FROM x,y
UNION ALL
SELECT y.name, x.ord FROM x,y
) z
WHERE t.name = z.name;
This query only updates if both rows can be found and does nothing otherwise.
UPDATE Table_1
SET ord =
CASE name
WHEN 'Pete' THEN (SELECT ord FROM Table_1 WHERE name = 'Steve')
WHEN 'Steve' THEN (SELECT ord FROM Table_1 WHERE name = 'Pete')
END
WHERE name IN ('Pete', 'Steve')
You can easily replace 'Pete' and 'Steve' with other names...
BEGIN TRANSACTION
UPDATE TABLENAME
SET ord = 9
where name = 'Pete'
UPDATE TABLENAME
SET ord = 7
where name = 'Steve'
COMMIT TRANSACTION
Use below script to swap values
IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL DROP TABLE #TempTable
CREATE TABLE #TempTable
(
ROW_ID INT IDENTITY(1,1),
SEQUENCE_NO INT,
ID INT
)
DECLARE #Id INT = 24780, --Row Id
DECLARE #NewPosition INT = -1; -- (Move Up or Move Down +1 for Up and -1 For Down)
DECLARE #SEQUENCE_NO INT = 0;
INSERT INTO #TempTable
SELECT SEQUENCE_NO ,ID
FROM TABLE_NAME S
WHERE ID = #Id
SET #SEQUENCE_NO = (SELECT SEQUENCE_NO FROM #TempTable)
INSERT INTO #TempTable
SELECT SEQUENCE_NO AS SNO,ID
FROM TABLE_NAME S
WHERE ID <> #Id
AND SEQUENCE_NO = (#SEQUENCE_NO + #NewPosition) -- (Move Up or Move Down +1 for Up and -1 For Down)
--Add check point here temp table to have 2 exact records
;WITH x AS (SELECT ID, SEQUENCE_NO FROM #TempTable WHERE ROW_ID = 1)
, y AS (SELECT ID, SEQUENCE_NO FROM #TempTable WHERE ROW_ID = 2)
UPDATE #TempTable
SET SEQUENCE_NO = z.SEQUENCE_NO
FROM (
SELECT x.ID, y.SEQUENCE_NO FROM x,y
UNION ALL
SELECT y.ID, x.SEQUENCE_NO FROM x,y
) z
WHERE #TempTable.ID = z.ID;
UPDATE SI
SET SI.SEQUENCE_NO = T.SEQUENCE_NO -- (Swap Values here)
FROM TABLE_NAME SI
JOIN #TempTable T ON SI.ID = T.ID

How can I improve this SQL query?

I ran into an interesting SQL problem today and while I came up with a solution that works I doubt it's the best or most efficient answer. I defer to the experts here - help me learn something and improve my query! RDBMS is SQL Server 2008 R2, query is part of an SSRS report that will run against about 100,000 rows.
Essentially I have a list of IDs that could have multiple values associated with them, the values being Yes, No, or some other string. For ID x, if any of the values are a Yes, x should be Yes, if they are all No, it should be No, if they contain any other values but yes and no, display that value. I only want to return 1 row per ID, no duplicates.
The simplified version and test case:
DECLARE #tempTable table ( ID int, Val varchar(1) )
INSERT INTO #tempTable ( ID, Val ) VALUES ( 10, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 11, 'N')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 11, 'N')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 12, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 12, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 12, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 13, 'N')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 14, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 14, 'N')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 15, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 16, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 17, 'F')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 18, 'P')
SELECT DISTINCT t.ID, COALESCE(t2.Val, t3.Val, t4.Val)
FROM #tempTable t
LEFT JOIN
(
SELECT ID, Val
FROM #tempTable
WHERE Val = 'Y'
) t2 ON t.ID = t2.ID
LEFT JOIN
(
SELECT
ID, Val FROM #tempTable
WHERE Val = 'N'
) t3 ON t.ID = t3.ID
LEFT JOIN
(
SELECT ID, Val
FROM #tempTable
WHERE Val <> 'Y' AND Val <> 'N'
) t4 ON t.ID = t4.ID
Thanks in advance.
Let's answer an easier problem: for each id, get the Val which is last in the alphabet. This will work if Y and N are the only values. And the query is much simpler:
SELECT t.ID, MAX(t.Val) FROM t GROUP BY t.ID;
So, reduce your case to the simple case. Use an enum (if your DB supports it) or break the value codes into another table with a collation column (in this case, you could have 1 for Y, 2 for N, and 999 for all other possible values, and you want the smallest). Then
SELECT ID, c.Val FROM
(SELECT t.ID, MIN(codes.collation) AS mx
FROM t join codes on t.Val = codes.Val GROUP BY t.ID) AS q
JOIN codes c ON mx=c.collation;
Here codes has two columns, Val and Collation.
You can also do this with a CTE type query, as long as you have the Values ordered as you want them. This approach has one join to a small lookup table and should be much, much faster than 3 self-joins.
WITH q AS (SELECT t.id, t.Val, ROW_NUMBER() AS r FROM t JOIN codes ON t.Val=codes.Val
PARTITION BY t.id ORDER BY codes.collation)
SELECT q.id, q.Val WHERE r=1;
I'd change it to this just to make it easier to read:
SELECT DISTINCT t.ID, COALESCE(t2.Val, t3.Val, t4.Val)
FROM #tempTable t
LEFT JOIN #tempTable t2 ON t.ID = t2.ID and t2.Val = 'Y'
LEFT JOIN #tempTable t3 ON t.ID = t3.ID and t3.Val = 'N'
LEFT JOIN #tempTable t4 ON t.ID = t4.ID and t4.Val <> 'Y' AND t4.Val <> 'N'
Gives the same results as your example.
I also looked at the execution plans for both and they looked exactly the same, I doubt you'd see any performance difference.
Try this:
;WITH a AS (
SELECT
ID,
SUM(CASE Val WHEN 'Y' THEN 1 ELSE 0 END) AS y,
SUM(CASE Val WHEN 'N' THEN 0 ELSE 1 END) AS n,
MIN(CASE WHEN Val IN ('Y','N') THEN NULL ELSE Val END) AS first_other
FROM #tempTable
GROUP BY ID
)
SELECT
ID,
CASE WHEN y > 0 THEN 'Y' WHEN n = 0 THEN 'N' ELSE first_other END AS Val
FROM a
If there are any 'Y' values then the sum of y will be greater than 0
If all values are 'N' then the sum of n will be zero
Get the first non 'Y' or 'N' character available if needed
In this case the result can be determined with only one pass through
the table
I'm reading your spec like this:
if any ID is Y then Y
if all IDs are N then N
else display value (other than Y or N)
eliminate rows per (1)
delete from #tempTable
where not Val='Y' and ID in (
select distinct ID
from #tempTable
where Val='Y'
)
select distinct to eliminate multiple N's per (2).
select distinct * from #tempTable
group up various "other" values to get a single row per ID.
SELECT A.Id, AllVals =
SubString(
(SELECT ', ' + B.Val
FROM C as B
WHERE A.Id = B.Id
FOR XML PATH ( '' ) ), 3, 1000)
FROM C as A
GROUP BY Id
Entire runnable query:
declare #tempTable table (ID int, Val char(1))
INSERT INTO #tempTable ( ID, Val ) VALUES ( 10, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 11, 'N')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 11, 'N')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 12, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 12, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 12, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 13, 'N')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 14, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 14, 'N')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 15, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 16, 'Y')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 17, 'F')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 18, 'P')
INSERT INTO #tempTable ( ID, Val ) VALUES ( 18, 'F')
delete from #tempTable
where not Val='Y' and ID in (
select distinct ID
from #tempTable
where Val='Y'
);
WITH C as (select distinct * from #tempTable)
SELECT A.Id, AllVals =
SubString(
(SELECT ', ' + B.Val
FROM C as B
WHERE A.Id = B.Id
FOR XML PATH ( '' ) ), 3, 1000)
FROM C as A
GROUP BY Id
OUTPUT:
Id AllVals
10 Y
11 N
12 Y
13 N
14 Y
15 Y
16 Y
17 F
18 F, P