SQL update column in row based on row number - sql

I am trying to sanitize a database I have for testing purposes, and want to update a column (name) to something like Name <rownumber>.
I have given it a go myself, but it cycles through all the rows and ends up naming them all the same at the end Name 323; 323 being the total number of rows.
Some direction would be very helpful.
This is what I have at the moment
Should I not be doing a while for each row in the table?
DECLARE #counter INT = 2
DECLARE #rows int
DECLARE #CustCode varchar(20)
DECLARE #CustName varchar(128)
DECLARE #CustID varchar(10)
SELECT #rows = COUNT(*) FROM Customer
WHILE #counter <= #rows
BEGIN
SELECT #CustID = CustomerID FROM Customer
SET #CustCode = 'CustCode' + #CustID
SET #CustName = 'Customer Description ' + #CustID
SET #CustName = 'Customer Description ' + #CustID
UPDATE Customer
SET CustomerCode = #CustCode,
CustomerDescription = #CustName
SET #counter = #counter + 1
END

Really, it would be much better to use ONE, set-based statement for this. You're not using any specific additional information (like a row number - as mentioned in your post) - you're just concatenating fixed text like Cust Code and Customer Description) with the already existing CustomerId - so a simple UPDATE statement like this would do (and would update everything in one go):
UPDATE dbo.Customer
SET CustomerCode = CONCAT('Cust Code ', CustomerId),
CustomerDescription = CONCAT('Customer Description ', CustomerId);

Related

Stored Procedure - Table Name as variable

I am tasked with writing a stored procedure that will validate the input data against a few tables before inserting into a main table named CHANGES.
Here is what I am dealing with table wise:
There is a lookup table. This table basically gives the user the rules of data validation before it can be inserted into the MAIN table. The lookup table looks like this:
ID TABLECODE COLUMNAME ACCEPTEDDATATYPE
1 luDEPT DEPTCODE INT
2 luEMP GENDERCODE INT
3 luDEPT BLDGcode INT
So if a user is inserting an ID of 1, we know they are trying to make a correction to the DeptCode column and they must meet the requirements that only an Integer will be accepted before inserting into the CHANGES table (this is the main table that will hold the new values).
CHANGES table - Data is inserted into this table with the new value per column. Data will only be inserted into this table if it passes validation against the lookup table and explained in part 3.
Structure of CHANGES table
ID pkid NEWVALUE
1 67 01
1 84 09
2 56 03
This is the part I would like some help/input with to even see if it's doable. The column from the LOOKUP table name TABLECODE is the name of an actual table that exists in the database with codes and description for each column. So for example, all the DEPTCODE codes will be found in a lookup table named: luDEPT
Here is how the luDEPT that looks like this:
CODE DEPARTMENTNAME
01 BIOLOGY
02 CHEMISTRY
03 INFORMATION TECHNOLOGY
So another validation step I have to take is, make sure that the NEW VALUE being inserted into CHANGES table is a valid code found in the lookup table related to the COLUMNNAME.
This is what I have so far, which works
CREATE PROCEDURE [dbo].[NewValueData]
(
#ID int,
#pkid VARCHAR(40),
#value VARCHAR(50)
)
AS
Begin
declare #result bit = 0;
declare #result1 bit = 0;
declare #result2 bit = 0;
declare #result3 bit = 0;
declare #result4 bit = 0;
DECLARE #tablename varchar(50);
DECLARE #columndatatype varchar(30);
set #columndatatype=(select accepteddatatype from lookup where ID=#ID)
**set #tablename=(select TABLE_NAME from INFORMATION_SCHEMA.TABLES A, lookup b
where a.TABLE_NAME= b.lutablecode
and TABLE_SCHEMA = 'Test' and ID=#ID)**
--CHECK IF ID, pkid and VALUE are PROVIDED
if (#pkid IS NULL OR #pkid = '') or (#ID IS NULL OR #ID = '') or (#value IS NULL OR #value =
'')
begin
set #result = 1
PRINT 'PKID,ID or Value is missing'
end
--CHECK IF ID EXISTS IN LOOKUP TABLE
if #ID not in (select ID from lookup
where #ID=ID)
begin
set #result1=1
PRINT 'ID is not in lookup table'
end
--IF datatype is an integer, only accept a numeric value
if #columndatatype = 'INT'
begin
set #result3 = IIF(ISNUMERIC(#value)=1,0,1)
PRINT 'column type is INT '
end
**--ATTEMPT of trying to use #tablename
--CHECK IF VALUE IS AN ACCEPTED VALUE IN THE LOOKUP TABLE FOR THAT COLUMN
if #value not in (select code from #tablename where #value=code)
begin
set #result4=1
PRINT 'Not a valid code')
end**
if (#result = 0 and #result1 = 0 and #result2 = 0 and #result3 = 0 and #result4 = 0)
begin
BEGIN TRANSACTION;
begin try
INSERT INTO [CHANGES] (ID, pkid,newvalue) VALUES (#ID, #pkid, #value)
PRINT 'New Record Inserted'
COMMIT TRANSACTION
end TRY
begin catch
ROLLBACK TRANSACTION
PRINT 'id is not acceptable'
END
end
GO
The text in bold is my attempt at trying to derive the tablename dynamically but it doesn't work. Does anyone have suggestion on how to go about this issue? Any help will be welcomed.
Try something like:
DECLARE #tablename sysname = 'luDEPT'
DECLARE #columnname sysname = 'DEPTCODE'
DECLARE #value INT = 123
DECLARE #valid BIT
DECLARE #sql NVARCHAR(MAX) = '
SET #Valid = CASE
WHEN EXISTS(SELECT * FROM ' + QUOTENAME(#tablename) + ' WHERE ' + QUOTENAME(#columnname) + ' = #Value)
THEN 1
ELSE 0
END'
EXECUTE sp_executesql
#sql,
N'#value INT, #valid BIT OUTPUT',
#value = #value, #valid = #valid OUTPUT
SELECT Valid = #Valid
The data type could potentially also be parameterized if types other than INT are needed.

How to dynamically provide column name and update the record in SQL Server?

I have a table like this
I want to store column name in varibale #myColumn and then use Update command. How can this be done ? Is it possible ?
DECLARE #myColumn varchar(20)
SET #myColumn = 'State'
UPDATE Country SET #myColumn = 'Florida' WHERE Id = 1
Update:
I asked this question for using on my local test database for my special requirement only. I wanted to know if that was possible. As some have mentioned about XY problem, I want to let everyone know this is a specific requirement for my local test database and is not for professional use.
To dynamically use the name of a column you'll need Dynamic SQL.
Here's an example:
DECLARE #myColumn SYSNAME
, #myValue VARCHAR(20)
, #myId INT;
DECLARE #DynSql NVARCHAR(MAX)
, #UpdateSql NVARCHAR(MAX)
, #UpdateParams NVARCHAR(MAX);
SET #myColumn = 'State';
SET #myValue = 'Florida';
SET #myId = 1;
SET #UpdateSql = 'UPDATE Country'+CHAR(10)
+ 'SET [COLUMN] = #Value' +CHAR(10)
+ 'WHERE Id = #Id';
SET #UpdateParams = '#Value varchar(20), #Id int';
SET #DynSql = REPLACE(#UpdateSql, '[COLUMN]', QUOTENAME(#myColumn));
-- SELECT #DynSql As DynSql;
EXECUTE sp_executesql #DynSql, #UpdateParams
, #Value = #myValue
, #Id = #myId;
SELECT * FROM Country WHERE ID = 1;
ID
CountryName
State
1
United States of America
Florida
Test on db<>fiddle here
Yes, it's possible with some dynamic SQL. If you have complete control in the process, you can try this solution:
Let's create a temp table to show:
create table #temp ([id] int, [state] varchar(10));
Insert into #temp
SELECT 1 as ID, null as [state]
select * from #temp
Now that's created, let's try
DECLARE #myColumn varchar(20), #sql varchar(400)
SET #myColumn = '[state]'
set #sql = CONCAT('Update #temp SET ',#myColumn ,'= ''FLORIDA'' where ID = 1')
exec(#sql)
Check the results
select * from #temp
If you don't have complete control over the process, you need to save your code from SQL Injection.

SQL server transfer column from one table to another

I am trying to transfer data from a column from one table to another, both columns are with unique identifiers and when i transfer the data it is copying the column after the end of the data of the second table. After the end of the other column of the second table (I am inserting first random integers in the first column and then I want to copy the information from another table in the same database and it is starting after the 135th row (I add 135 rows with random ints)). First table name : carBrand and column name model_id - second table name Cars11, model_idss - or whatever is the name...
THE QUESTION IS WHY is it inputting the iformation after the first input - example - i am inputting 135 random ints and after that i am trying to copy the information from the other table and when i am pasting it, the information is pasted after the 136th to the 270th sign
My query is looking like this for the new table
DECLARE #Min_Value AS int
SET #Min_Value= 15000
DECLARE #Max_Value AS int
SET #Max_Value = 1000000
DECLARE #n AS int
SET #n = 135
BEGIN TRANSACTION
DECLARE #uid uniqueidentifier
SET #uid = NEWID()
DECLARE #i AS int
SET #i = 1
WHILE #i <= #n BEGIN INSERT INTO Cars11([Model_prices]) VALUES(FLOOR(RAND(CHECKSUM(#uid))*(#Max_Value - #Min_Value +1) + #Min_Value)) SET #i += 1 SET #uid = NEWID() END COMMIT TRANSACTION
INSERT INTO Cars11(model_idss)
SELECT model_id
FROM carBrand
WHERE model_id <= 135;
It would be easier to parse your query if you used the code sample block (ctrl-k)
You need to do an update instead of insert for the second insert into Cars11.
Update changes already existing records, Insert creates new records.
Something like this:
DECLARE #Min_Value AS int
SET
#Min_Value = 15000
DECLARE #Max_Value AS int
SET
#Max_Value = 1000000
DECLARE #n AS int
SET
#n = 135
BEGIN TRANSACTION
DECLARE #uid uniqueidentifier
SET
#uid = NEWID()
DECLARE #i AS int
SET
#i = 1 WHILE #i <= #n
BEGIN
INSERT INTO
Cars11([Model_prices])
VALUES
(
FLOOR(
RAND(CHECKSUM(#uid)) *(#Max_Value - #Min_Value + 1) + #Min_Value
)
)
SET
#i + = 1
SET
#uid = NEWID()
END
COMMIT TRANSACTION
UPDATE Cars11
Set model_idss = (Select model_id FROM carBrand WHERE Cars11.model_idss = carBrand.model_id and carBrand.model_id <= 135));
Here are some other options for updating a column based on a query result

Can SQL use calculate values as alias names

I am trying to find out how to use a calculated value as part of an alias name .
For example:
Select employeeName as ''name of guy' but where 'name of guy' could be getdate() + 'name o guy'
Addin qualifiers just prevents the code inside from calculating.
Is this possible in any way? I'm going to use a partition to group results by year and I need the column aliases to be based on the year thy are in
I don't know if it's what you are looking for but it could be a good starting :
DECLARE #var table(rownum int, rowkey varchar(60), ALIAS varchar(80))
INSERT INTO #var SELECT row_number() over (ORDER BY employeeName), employeeName, cast(getdate() AS varchar(12))+employeeName FROM Table1
DECLARE #query varchar(500)
DECLARE #rowkey varchar(60), #i int, #max int
SET #i = 1
SET #max = (SELECT count(*) FROM #var)
SET #query = ''
WHILE #i <= #max
BEGIN
SET #rowkey = (SELECT rowkey FROM #var WHERE rownum = #i)
SET #query = 'select employeeName as "'+(SELECT ALIAS FROM #var WHERE rowkey = #rowkey)+'" from Table1 where employeeName = '''+#rowkey+''';'
exec(#query)
SET #i = #i + 1
END;
If you're only expecting a single value, you could use a table variable. However, if multiple rows are created it effectively becomes a temporary table. If you think that's likely, declare as a temporary table.

Using variable name to run query on multiple tables

what I am trying to do is run a query multiple times over multiple tables, so what I have here is a table of the table names that cycles through setting #tablename to the name of the table on each iteration that I want to run the query on.
As you can see below #tablename is the name of the table I want to run the queries on but how do i run these queries using #tablename as the table name?
CREATE TABLE [BusinessListings].[dbo].[temptablenames]
(id int,
name nvarchar(50),
)
INSERT INTO [BusinessListings].[dbo].[temptablenames] (id, name)
VALUES
(1,'MongoOrganisationsACT1'),
(2,'MongoOrganisationsNSW1'),
(3,'MongoOrganisationsNT1'),
(4,'MongoOrganisationsQLD1'),
(5,'MongoOrganisationsSA1'),
(6,'MongoOrganisationsTAS1'),
(7,'MongoOrganisationsVIC1'),
(8,'MongoOrganisationsWA1');
DECLARE #tablename sysname,
#id int
SET #id = 1
WHILE (#id < 9)
BEGIN
select #tablename = name from temptablenames where id = #id
select #tablename
select _key_out, sum(quality_score) as sumscore, count(*) as reccount, (sum(quality_score) / count(*)) as ave
into tempga0
from #tablename
group by _key_out
select _key_out, count(*) as reccount
into tempga3
from #tablename
where dedupe_result is null
group by _key_out
having count(*)>1
select a._key_out, max(quality_score) as maxdedupetotalscore
into tempga4
from
#tablename a
join
tempga3 b
on a._key_out = B._key_out
--where isdeleted is null
group by a._key_out
--- keep records
update #tablename
set dedupe_result = 'Keep'
from
#tablename a
join
tempga4 b
on a._key_out = B._key_out
where a.quality_score = b.maxdedupetotalscore
--and isdeleted is null
and dedupe_result is null
SET #id = #id + 1
END
GO
DROP TABLE [BusinessListings].[dbo].[temptablenames]
note: this is only part of the queries that I want run, I just want to figure out how to subsitute the variable in the query as the table name. Also I know this isnt good form but there is a reason I need to do it this way.
updated working code here:
DECLARE #tablename nvarchar(30),
#id int,
#SQLStr nvarchar(1000)
SET #id = 1
WHILE (#id < 9)
BEGIN
select #tablename = name from temptablenames where id = #id
IF OBJECT_ID('tempga0') IS NOT NULL
DROP TABLE tempga0
set #SQLStr = 'select _key_out, sum(quality_score) as sumscore, count(*) as reccount, (sum(quality_score) / count(*)) as ave
into tempga0
from ' + #tablename + ' group by _key_out'
exec(#SQLStr)
SET #id = #id + 1
END
GO
Use the Exec command. Write your query in a variable like and execute it
Declare #SQLStr = 'Select * into X from ' + #tablename
exec(#SQLStr)
You just have to be carefull. I see that you are using into statements. You will have to check that the table does not already exist because you will get an exception. You will need to drop the tables, or a better way would be to do this before you start your loop:
CREATE TABLE tempga0 (
_key_out int,
sumscore numeric(18,9),
reccount int,
ave numeric(18,9))
--rest of the tables to be created here...
Create all the tables, and when you start your While loop add a
WHILE (#id < 9)
BEGIN
TRUNCATE TABLE tempga0
--truncate the rest of the tables
--Do the rest of your stuff here
END
Hope it helps