Write a query to swap values in a table in SQL Server? - sql

I have mistakenly inserted wrong data in table rows, now I want to swap the data.
Male in place of Female and vice-versa.
Following is the correct data I am expecting -

Simple update works:
UPDATE myTable
SET
col1 = CASE WHEN col1 = 'male' THEN 'female' ELSE 'male' END,
col2 = CASE WHEN col2 = 'female' THEN 'male' ELSE 'female' END
Result: row values will be swap.
I hope, It will work for you.

You could use:
UPDATE table_name
SET Gender = CASE Gender
WHEN 'Male' THEN 'Female'
WHEN 'Female' THEN 'Male'
ELSE Gender
END;
LiveDemo
Please note that other values than male/female like N/A or NULL will remain the same.
If you want to do it only for specified names use:
UPDATE table_name
SET Gender = CASE
WHEN Name IN ('Geetha', 'Radha') THEN 'Female'
WHEN Name IN ('Ram', 'Syam', 'Ravi') THEN 'Male'
END
WHERE Name IN ('Ram', 'Syam', 'Geetha', 'Radha', 'Ravi');

I don't understand why you want a temp table if you can do it with a single update
But maybe this fits what you are looking for
SELECT TOP 0 *
INTO #tmp
FROM YourTable
UPDATE T
SET gender = 'male'
OUTPUT Inserted.* INTO #tmp
FROM YourTable T
WHERE gender = 'female'
UPDATE T
SET gender = 'female'
FROM YourTable
WHERE gender = 'male' --Irrelevant for this case but assuming that you have different values
AND NOT EXISTS (SELECT 1 FROM #Temp WHERE name = T.name) -- Exclude the "swapped" records
Or maybe you just want a temp table to confirm the changes before update them.... i am trying to guess
DECLARE #swap1 VARCHAR(20) = 'male', #swap2 VARCHAR(20) = 'female'
SELECT *
INTO #tmp
FROM YourTable
UPDATE #tmp
SET gender = CASE
WHEN gender = #swap1 THEN #swap2
WHEN gender = #swap2 THEN #swap1
ELSE gender END
SELECT * FROM #tmp
UPDATE A
SET Gender = T.gender
FROM YourTable A
JOIN #tmp T ON A.name = T.name
DROP TABLE #tmp

Use below script to swap values between 2 rows
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

Related

Inserting value based on any condition in SQL

declare #varID as INT = 2
insert into table1
(
ID
,varName
)
select
ID
,**#####**
from table2
I want value of varName based on #varID. How can I do that?
What should be in place of #####?
Statement that replicate IF #varname = 2 then 'P' else 'I'.
You are very close. Just mention the variable in the SELECT clause. Like this.
declare #varID as INT = 2
insert into table1 (ID ,varName)
select ID,
CASE WHEN #varID = 2 THEN 'P' ELSE 'I' END
from table2
Maybe a CASE WHEN THEN ELSE END statement like below
declare #varID as INT = 2
insert into table1
(
ID
,varName
)
select
ID
,CASE WHEN #varID=2 THEN 'P' ELSE 'I' END
from table2

How to copy records from table and insert into same table using stored procedure SQL Server

I have following table:
I want to copy only those records which are from version 0 and their student_id is never repeated in version 1, that means unchanged records. and I want to insert all copied records to same table with version 1. What will be stored procedure for this.
using group by and having max(version) = 0:
insert into student_name (student_id, student_name, version)
select student_id, max(student_name), 1
from student_name
group by student_id
having max(version) = 0
As a stored procedure, taking a parameter for version, that inserts records for students who do not have a record for that version: and outputs the rows that were inserted:
create procedure dbo.insert_new_version (#version int) as
begin;
set nocount, xact_abort on;
insert into student_name (student_id, student_name, version)
output inserted.*
select
student_id
, student_name = max(student_name)
, version = #version
from student_name
group by student_id
having max(version) < #version
end;
rextester demo: http://rextester.com/JSTNI40605
returns:
+-----------+------------+--------------+---------+
| record_id | student_id | student_name | version |
+-----------+------------+--------------+---------+
| 11 | 3 | ccc | 1 |
+-----------+------------+--------------+---------+
You can select the records by doing:
select t.*
from t
where t.version = 0 and
not exists (select 1
from t t2
where t2.student_id = t.student_id and t2.version = 1
);
The rest is just an insert.
I'd suggest using a while loop to go through the table and identify the items that you need to copy, then these values will be
evaluated and if they meet the criterion for re-inserting and then insert them.
Your code should look like the following. Add CREATE PROC part and Edit table names where applicable
(Caveat: I have written this on notepad so if you get a few errors, just try to fix them)
DECLARE #counter int = 0, #row_Count int = 0, #currentId int,
#currentName nvarchar(100), #version int, #current_Student_id int
SET #row_Count = (SELECT COUNT(record_Id) from yourTable)
WHILE #counter <= #row_Count
BEGIN
SET #currentId = (SELECT record_Id FROM (SELECT row_number() over (order by id)
AS RowNum, record_Id FROM yourTable) sub WHERE RowNum=#counter)
SET #currentName = (SELECT student_name FROM yourTable WHERE record_Id = #currentId)
SET #current_Student_id = (SELECT student_id FROM yourTable WHERE record_Id = #currentId)
SET #version = (SELECT version FROM yourTable WHERE record_Id = #currentId)
--USE IF to check if the current version is 0 and the student ID has not been inserted already
IF (SELECT COUNT(record_Id) FROM yourTable WHERE student_id = #current_Student_id AND version = 1) < 1
AND #version = 0
BEGIN
INSERT INTO yourTable (student_id, student_name, version)
VALUES
(
#current_Student_id,
#currentName,
1
)
END
SET #counter = #counter + 1;
END
You can select and insert like this
Insert INTO tableName select t1.student_Id, t1.student_name,1 from tablename t1
where t1.version = 0 and not exists
(select 1 from tablename t2 where t2.student_id = t.student_id and t2.version = 1);
You can try this by LEFT Join:
INSERT INTO tbl
SELECT T1.record_id,T1.student_Id,T1.student_name, 1
FROM tbl T1 LEFT JOIN tbl T2
ON T1.student_Id = T2.student_Id AND T2.version = 1
WHERE T1.version = 0 AND T2.record_id IS NULL

Getting Status Count for an item with many statuses

Lets say I have the following table variable:
DECLARE #DevicesAndStatuses TABLE (Id BIGINT,[Status] INT);
DECLARE #myId BIGINT;
SET #myId = 1;
Inside above table I can have thousands of Ids(Id can be repeated) and statuses ranging between 1-50. What is the most efficient way of getting the count of all the statuses for a particular Id?
The traditional method which I have is as follows:
SELECT
(SELECT COUNT(*) FROM #DevicesAndStatuses WHERE Id = #myId AND [Status] = 1) AS Status1,
(SELECT COUNT(*) FROM #DevicesAndStatuses WHERE Id = #myId AND [Status] = 2) AS Status2,
(SELECT COUNT(*) FROM #DevicesAndStatuses WHERE Id = #myId AND [Status] = 3) AS Status3,
(SELECT COUNT(*) FROM #DevicesAndStatuses WHERE Id = #myId AND [Status] = 4) AS Status4,
...
(SELECT COUNT(*) FROM #DevicesAndStatuses WHERE Id = #myId AND [Status] = 50) AS Status50,
FROM #DevicesAndStatuses WHERE Id = #myId
Are there potentially any better solution for getting the count of all the statuses [1-50] for a particular id?
Final result should be a single row containing 50 columns showing the count() of every status as Status1,Status2,...,Status50.*
My first suggestion is to use a group by:
SELECT status, count(*)
FROM #DevicesAndStatuses
WHERE Id = #myId
GROUP BY status;
The simplest way to get the information you want, but in multiple rows.
If you want multiple columns, then use conditional aggregation:
SELECT SUM(CASE WHEN [Status] = 1 THEN 1 ELSE 0 END) AS Status1,
SUM(CASE WHEN [Status] = 2 THEN 1 ELSE 0 END) AS Status2,
SUM(CASE WHEN [Status] = 3 THEN 1 ELSE 0 END) AS Status3,
SUM(CASE WHEN [Status] = 4 THEN 1 ELSE 0 END) AS Status4,
. . .
FROM #DevicesAndStatuses
WHERE Id = #myId
Sure:
SELECT Status, COUNT(*)
FROM #DevicesAndStatuses
WHERE Id = #myId
GROUP BY Status
This returns all Status values for Id = #myId, and their count - in one simple statement
You'll need to use a Dynamic Pivot Query to achieve this:
I've done it using a generic example but poke me if you need a more specific version. You'll need to use a Temp Table instead of a Table Variable though.
The STUFF command is there to remove the , from the beginning of the strings.
CREATE TABLE #Items
(
Item INT IDENTITY(1,1),
[Status] INT
)
INSERT #Items
(Status)
VALUES
(1),(1),(1),(1),(1),(1),(1),(2),(2),(2),(2),(3),(3),(4),(4),(4),(4),(4),(4),(4),(4),(5);
DECLARE #StatusList NVARCHAR(MAX) = N'',
#SumSelector NVARCHAR(MAX) = N''
SELECT #StatusList = CONCAT(#StatusList, N',', QUOTENAME(s.Status)),
#SumSelector = CONCAT(#SumSelector, N',', N'SUM(', QUOTENAME(s.Status), N') AS Status_', s.Status)
FROM (SELECT DISTINCT [Status] FROM #Items) AS s
SELECT #StatusList = STUFF(#StatusList, 1, 1, N''),
#SumSelector = STUFF(#SumSelector, 1, 1, N'')
DECLARE #StatusPivotQuery NVARCHAR(MAX) = CONCAT(N'
SELECT ', #SumSelector, N'
FROM #Items AS s
PIVOT
(
COUNT(s.[Status])
FOR s.[Status] IN(', #StatusList, N')
) AS pvt ')
EXEC sys.sp_executesql #StatusPivotQuery
DROP TABLE #Items
Here you go
SELECT MAX(id) AS Id, status, COUNT(*)
FROM #DevicesAndStatuses
WHERE Id = #myId
GROUP BY status;
or
SELECT id AS Id, status, COUNT(*)
FROM #DevicesAndStatuses
WHERE Id = #myId
GROUP BY id,status;

SQL - populate new column according to data in row above

I need to populate a new column in a table known as RowType, where if the ID column contains the same ID value as the one above RowType is populated with 'D', if the value is new then RowType is populate with 'H', how would the SQL code look to be able to do this?
I.e should look something like below:
RowType (to be populated), ID (already there)
H, 1
D, 1
D, 1
H, 2
D, 2
H, 3
D, 3
D, 3
Thanks
You can use Row_Number and case
select *, RowType = case when Row_Number() over (partition by id order by id) = 1 then 'H' else 'D' End from #yourid
Your input table:
create table #yourId (id int)
insert into #yourid (id) values
(1)
,(1)
,(1)
,(2)
,(2)
,(3)
,(3)
,(3)
Use ROW_NUMER concept :
CREATE TABLE #table(Id INT)
INSERT INTO #table(Id)
SELECT 1 UNION ALL
SELECT 1 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 3 UNION ALL
SELECT 3
SELECT CASE WHEN RowType = 1 THEN 'H' ELSE 'D' END RowType , Id
FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY Id ORDER BY id) RowType , Id
FROM #table
) A
Please try...
UPDATE tableName
SET RowType = CASE
WHEN ( ID = LAG( ID ) OVER ( ORDER BY ID ) ) THEN 'D'
ELSE 'H'
END
If you have any questions or comments, then please feel free to post a Comment accordingly.
Further Reading
https://learn.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql (for information on LAG()).
It may not be the best solution, however it can point you somewhere, and it works.
Go through the code carfuly and make sure you understand this.
create table yourTable (RowType char, id int)
insert into yourTable (RowType, id) values
('',1)
,('',1)
,('',1)
,('',2)
,('',2)
,('',3)
,('',3)
,('',3)
select
row_number() over (order by id) as rowNumber,
RowType,
id
into #tempTable
from yourTable
declare #maxRow int = (select max(rowNumber) from #tempTable)
declare #currentRow int = 1
while (#currentRow <= #maxRow)
begin
if (#currentRow = 1)
begin
update #tempTable
set RowType = 'H'
where rowNumber = #currentRow
end
else
begin
if (select id from #tempTable where rowNumber = #currentRow) = (select id from #tempTable where rowNumber = #currentRow - 1)
begin
update #tempTable
set RowType = 'D'
where rowNumber = #currentRow
end
else
begin
update #tempTable
set RowType = 'H'
where rowNumber = #currentRow
end
end
set #currentRow = #currentRow +1
end
-- update data in actual table, you can do below if only those two columns exist in table !!!
delete from yourTable
-- insert into table from updated temp table
insert into yourTable
select RowType, ID
from #tempTable
select * from yourTable
select * from #tempTable
-- drop temp table
drop table #tempTable

SQL Switch/Case in 'where' clause

I tried searching around, but I couldn't find anything that would help me out.
I'm trying to do this in SQL:
declare #locationType varchar(50);
declare #locationID int;
SELECT column1, column2
FROM viewWhatever
WHERE
CASE #locationType
WHEN 'location' THEN account_location = #locationID
WHEN 'area' THEN xxx_location_area = #locationID
WHEN 'division' THEN xxx_location_division = #locationID
I know that I shouldn't have to put '= #locationID' at the end of each one, but I can't get the syntax even close to being correct. SQL keeps complaining about my '=' on the first WHEN line...
How can I do this?
declare #locationType varchar(50);
declare #locationID int;
SELECT column1, column2
FROM viewWhatever
WHERE
#locationID =
CASE #locationType
WHEN 'location' THEN account_location
WHEN 'area' THEN xxx_location_area
WHEN 'division' THEN xxx_location_division
END
without a case statement...
SELECT column1, column2
FROM viewWhatever
WHERE
(#locationType = 'location' AND account_location = #locationID)
OR
(#locationType = 'area' AND xxx_location_area = #locationID)
OR
(#locationType = 'division' AND xxx_location_division = #locationID)
Here you go.
SELECT
column1,
column2
FROM
viewWhatever
WHERE
CASE
WHEN #locationType = 'location' AND account_location = #locationID THEN 1
WHEN #locationType = 'area' AND xxx_location_area = #locationID THEN 1
WHEN #locationType = 'division' AND xxx_location_division = #locationID THEN 1
ELSE 0
END = 1
I'd say this is an indicator of a flawed table structure. Perhaps the different location types should be separated in different tables, enabling you to do much richer querying and also avoid having superfluous columns around.
If you're unable to change the structure, something like the below might work:
SELECT
*
FROM
Test
WHERE
Account_Location = (
CASE LocationType
WHEN 'location' THEN #locationID
ELSE Account_Location
END
)
AND
Account_Location_Area = (
CASE LocationType
WHEN 'area' THEN #locationID
ELSE Account_Location_Area
END
)
And so forth... We can't change the structure of the query on the fly, but we can override it by making the predicates equal themselves out.
EDIT: The above suggestions are of course much better, just ignore mine.
The problem with this is that when the SQL engine goes to evaluate the expression, it checks the FROM portion to pull the proper tables, and then the WHERE portion to provide some base criteria, so it cannot properly evaluate a dynamic condition on which column to check against.
You can use a WHERE clause when you're checking the WHERE criteria in the predicate, such as
WHERE account_location = CASE #locationType
WHEN 'business' THEN 45
WHEN 'area' THEN 52
END
so in your particular case, you're going to need put the query into a stored procedure or create three separate queries.
OR operator can be alternative of case when in where condition
ALTER PROCEDURE [dbo].[RPT_340bClinicDrugInventorySummary]
-- Add the parameters for the stored procedure here
#ClinicId BIGINT = 0,
#selecttype int,
#selectedValue varchar (50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
drugstock_drugname.n_cur_bal,drugname.cdrugname,clinic.cclinicname
FROM drugstock_drugname
INNER JOIN drugname ON drugstock_drugname.drugnameid_FK = drugname.drugnameid_PK
INNER JOIN drugstock_drugndc ON drugname.drugnameid_PK = drugstock_drugndc.drugnameid_FK
INNER JOIN drugndc ON drugstock_drugndc.drugndcid_FK = drugndc.drugid_PK
LEFT JOIN clinic ON drugstock_drugname.clinicid_FK = clinic.clinicid_PK
WHERE (#ClinicId = 0 AND 1 = 1)
OR (#ClinicId != 0 AND drugstock_drugname.clinicid_FK = #ClinicId)
-- Alternative Case When You can use OR
AND ((#selecttype = 1 AND 1 = 1)
OR (#selecttype = 2 AND drugname.drugnameid_PK = #selectedValue)
OR (#selecttype = 3 AND drugndc.drugid_PK = #selectedValue)
OR (#selecttype = 4 AND drugname.cdrugclass = 'C2')
OR (#selecttype = 5 AND LEFT(drugname.cdrugclass, 1) = 'C'))
ORDER BY clinic.cclinicname, drugname.cdrugname
END
Please try this query.
Answer To above post:
select #msgID, account_id
from viewMailAccountsHeirachy
where
CASE #smartLocationType
WHEN 'store' THEN account_location
WHEN 'area' THEN xxx_location_area
WHEN 'division' THEN xxx_location_division
WHEN 'company' THEN xxx_location_company
END = #smartLocation
Try this:
WHERE (
#smartLocationType IS NULL
OR account_location = (
CASE
WHEN #smartLocationType IS NOT NULL
THEN #smartLocationType
ELSE account_location
END
)
)
CREATE PROCEDURE [dbo].[Temp_Proc_Select_City]
#StateId INT
AS
BEGIN
SELECT * FROM tbl_City
WHERE
#StateID = CASE WHEN ISNULL(#StateId,0) = 0 THEN 0 ELSE StateId END ORDER BY CityName
END
Try this query, it's very easy and useful: Its ready to execute!
USE tempdb
GO
IF NOT OBJECT_ID('Tempdb..Contacts') IS NULL
DROP TABLE Contacts
CREATE TABLE Contacts(ID INT, FirstName VARCHAR(100), LastName VARCHAR(100))
INSERT INTO Contacts (ID, FirstName, LastName)
SELECT 1, 'Omid', 'Karami'
UNION ALL
SELECT 2, 'Alen', 'Fars'
UNION ALL
SELECT 3, 'Sharon', 'b'
UNION ALL
SELECT 4, 'Poja', 'Kar'
UNION ALL
SELECT 5, 'Ryan', 'Lasr'
GO
DECLARE #FirstName VARCHAR(100)
SET #FirstName = 'Omid'
DECLARE #LastName VARCHAR(100)
SET #LastName = ''
SELECT FirstName, LastName
FROM Contacts
WHERE
FirstName = CASE
WHEN LEN(#FirstName) > 0 THEN #FirstName
ELSE FirstName
END
AND
LastName = CASE
WHEN LEN(#LastName) > 0 THEN #LastName
ELSE LastName
END
GO
In general you can manage case of different where conditions in this way
SELECT *
FROM viewWhatever
WHERE 1=(CASE <case column or variable>
WHEN '<value1>' THEN IIF(<where condition 1>,1,0)
WHEN '<value2>' THEN IIF(<where condition 2>,1,0)
ELSE IIF(<else condition>,1,0)
END)
Case Statement in SQL Server Example
Syntax
CASE [ expression ]
WHEN condition_1 THEN result_1
WHEN condition_2 THEN result_2
...
WHEN condition_n THEN result_n
ELSE result
END
Example
SELECT contact_id,
CASE website_id
WHEN 1 THEN 'TechOnTheNet.com'
WHEN 2 THEN 'CheckYourMath.com'
ELSE 'BigActivities.com'
END
FROM contacts;
OR
SELECT contact_id,
CASE
WHEN website_id = 1 THEN 'TechOnTheNet.com'
WHEN website_id = 2 THEN 'CheckYourMath.com'
ELSE 'BigActivities.com'
END
FROM contacts;
This worked for me.
CREATE TABLE PER_CAL ( CAL_YEAR INT, CAL_PER INT )
INSERT INTO PER_CAL( CAL_YEAR, CAL_PER ) VALUES ( 20,1 ), ( 20,2 ), ( 20,3 ), ( 20,4 ), ( 20,5 ), ( 20,6 ), ( 20,7 ), ( 20,8 ), ( 20,9 ), ( 20,10 ), ( 20,11 ), ( 20,12 ),
( 99,1 ), ( 99,2 ), ( 99,3 ), ( 99,4 ), ( 99,5 ), ( 99,6 ), ( 99,7 ), ( 99,8 ), ( 99,9 ), ( 99,10 ), ( 99,11 ), ( 99,12 )
The 4 digit century is determined by the rule, if the year is 50 or more, the century is 1900, otherwise 2000.
Given two 6 digit periods that mark the start and end period, like a quarter, return the rows that fall in that range.
-- 1st quarter of 2020
SELECT * FROM PER_CAL WHERE (( CASE WHEN CAL_YEAR > 50 THEN 1900 ELSE 2000 END + CAL_YEAR ) * 100 + CAL_PER ) BETWEEN 202001 AND 202003
-- 4th quarter of 1999
SELECT * FROM PER_CAL WHERE (( CASE WHEN CAL_YEAR > 50 THEN 1900 ELSE 2000 END + CAL_YEAR ) * 100 + CAL_PER ) BETWEEN 199910 AND 199912
Try this query. Its very easy to understand:
CREATE TABLE PersonsDetail(FirstName nvarchar(20), LastName nvarchar(20), GenderID int);
GO
INSERT INTO PersonsDetail VALUES(N'Gourav', N'Bhatia', 2),
(N'Ramesh', N'Kumar', 1),
(N'Ram', N'Lal', 2),
(N'Sunil', N'Kumar', 3),
(N'Sunny', N'Sehgal', 1),
(N'Malkeet', N'Shaoul', 3),
(N'Jassy', N'Sohal', 2);
GO
SELECT FirstName, LastName, Gender =
CASE GenderID
WHEN 1 THEN 'Male'
WHEN 2 THEN 'Female'
ELSE 'Unknown'
END
FROM PersonsDetail