This query is to prove a concept that I will eventually use to locate all columns with a specific value and then create a name/value pair for export to JSON. But I'm stuck.
I query the list of all columns from the sql table. I would then like to go through the columns in Table1 row by row and update the values using the variable to construct the query. For example as it reads through the list if Col4 = "Old text" then I would like to set the value of Col 4 = "New Text"
DECLARE #c varCHAR(100)
DECLARE ReadData CURSOR
FOR SELECT cname FROM sys.syscolumns WHERE creator = 'dbserver' AND tname = 'Table1'
DECLARE #RowCount INT
SET #RowCount = (SELECT COUNT(cname) FROM sys.syscolumns WHERE creator = 'dbserver' AND tname = 'Table1')
OPEN ReadData
DECLARE #I INT // iterator
SET #I = 1 // initialize
WHILE (#I <= #RowCount)
BEGIN
FETCH NEXT ReadData INTO #c
INSERT INTO serverdb.Table2 (cname)VALUES(#c)// this works inserting all 100 columns in the cname column of Table 2
UPDATE serverdb.Table1 SET #c = 'New text' WHERE #c = 'Old text'// this fails with a syntax error. #c is not being interpreted for the query. Note: If I hard code the #c var (for testing)to a known column name, the query works as well
SET #I = #I + 1
END;
Why won't the update statement recognize the variable? What am I missing?
When you use varibale as mentioned below it is considered as a character string.
UPDATE serverdb.Table1 SET #c = 'New text' WHERE #c = 'Old text'
You need to create a dynamic query. use the execute method to execute your dynamic query
declare #sql varchar(999)
SELECT #sql = 'UPDATE serverdb.Table1 SET '+ #c + '= ''New text'' WHERE '+ #c+ ' = ''Old text'' '
execute(#sql)
Hope this helps
Related
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.
This is my code:
DECLARE #LoopCounter INT = 1,#max INT,#table nvarchar(100),#academic nvarchar(100)
SELECT #max = max(id)
FROM #tablelist
WHILE(#LoopCounter <= #max)
BEGIN
SET #table = (SELECT TABLE_NAME
FROM #tablelist WHERE id = #LoopCounter)
SET #academic = (SELECT F6
FROM #table WHERE F5 = Academic)
SET #LoopCounter = #LoopCounter + 1
END
where #tablelist is a list of all my tables in the database that are relevent.
The step:
SET #academic = (SELECT F6
FROM #table WHERE F5 = Academic)
does not work... I've tried with sp_execute but I get thrown back standard errors, at the moment the error is 'Must declare the standard variable '#table'. I have also tried an EXEC but it doesn't like that either.
What I want to do is define #academic to be a value from the #table The problem I'm having is the SET does not like being from #table.
Any help would be fab!
I think you want dynamic SQL:
DECLARE #LoopCounter INT = 1,
#max INT,#table nvarchar(100),
#academic nvarchar(100);
DECLARE #SQL NVARCHAR(MAX);;
SELECT #max = max(id)
FROM #tablelist;
WHILE(#LoopCounter <= #max)
BEGIN
SET #table = (SELECT TABLE_NAME FROM #tablelist WHERE id = #LoopCounter) ;
SET #SQL = '
SELECT #academic = F6
FROM [table]
WHERE f5 = 'Academic'
';
SET #SQL = REPLACE(#SQL, '[Table]', #table_name);
EXEC sp_executesql #SQL,
N'#academic NVARCHAR(100)',
#academic=#academic;
SET #LoopCounter = #LoopCounter + 1
END;
All that said, this is not an example of good coding (even ignoring the fact that I'm not escaping the table name).
You should not have multiple tables with the same columns, distinguished only by a variant of the name. Instead, you should have a single table with all the rows, and perhaps an additional column or two for identifying the columns.
Having to use dynamic SQL can illustrate a problem with the data model. And in this case, I think that it does.
I have the following procedure:
CREATE PROCEDURE alterFieldSize #column NVARCHAR(MAX), #table NVARCHAR(MAX), #prsColumnLen INT
AS
-- DECLARE VARIABLES
DECLARE #sql NVARCHAR(MAX)
DECLARE #fieldLen INT
DECLARE #columnLen INT
-- SET VARIABLES
SET #fieldLen = #prsColumnLen
SET #columnLen = (SELECT CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME LIKE #column AND TABLE_NAME LIKE #table)
SET #fieldLen = CASE WHEN #columnLen > #fieldLen THEN #columnLen WHEN #fieldLen <=500 THEN #fieldLen ELSE 500 END
SET #sql = 'ALTER TABLE Client ALTER COLUMN '+ #column + ' NVARCHAR('+cast(#fieldLen as VARCHAR(20))+')'
-- Execution
EXEC(#sql)
Which is then called via:
SET #fieldLen = (SELECT MAX(LEN(s.StreetAddress)) FROM [DB1].dbo.Sites s)
EXEC alterFieldSize #column = 'ClientAddress', #table = 'Client', #prsColumnLen = #fieldLen
Ofcourse this runs into issues because if the field values are set to 500 then i cant insert the data accross due to the field being too small in size unless i do this manually on each insert statement. What i want is to take everything after the 500th character out if the field is set to 500 during that case.
Thanks in advance
Variant 1: create new persisted computed column with maximal lenght of 500
alter table add NewPersistedColumn as (left(YourColumn, 500) persisted
Variant 2: make instead of insert trigger with left function
create trigger trg_table on trg_table
instead of insert
as
insert into table (col, col2) select left(col, 500), col2 from inserted.*
Best solution is to avoid this string in your app. Your solution should depend on your purpose of longer and shorter field.
I have a stored procedure which takes 'table name' as parameter. I want to store my 'exec' results to a variable and display using that variable.
Here is my T-SQL stored procedure..
create procedure DisplayTable( #tab varchar(30))
as
begin
Declare #Query VARCHAR(30)
set #Query='select * from ' +#tab
EXEC (#Query)
END
I want to do something like this..
SET #QueryResult = EXEC (#Query)
select #QueryResult
How do i achieve this.. Please help.. I am a beginner..
You can use XML for that. Just add e.g. "FOR XML AUTO" at the end of your SELECT. It's not tabular format, but at least it fulfills your requirement, and allows you to query and even update the result. XML support in SQL Server is very strong, just make yourself acquainted with the topic. You can start here: http://technet.microsoft.com/en-us/library/ms178107.aspx
alter procedure DisplayTable(
#tab varchar(30)
,#query varchar(max) output
)
as
BEGIN
Declare #execution varchar(max) = 'select * from ' +#tab
declare #tempStructure as table (
pk_id int identity
,ColumnName varchar(max)
,ColumnDataType varchar(max)
)
insert into
#tempStructure
select
COLUMN_NAME
,DATA_TYPE
from
INFORMATION_SCHEMA.columns
where TABLE_NAME= #tab
EXEC(#execution)
declare #ColumnCount int = (SELECT count(*) from #tempStructure)
declare #counter int = 1
while #counter <= #ColumnCount
BEGIN
IF #counter = 1
BEGIN
set #query = (SELECT ColumnName + ' ' + ColumnDataType FROM #tempStructure where pk_id= #counter)
END
IF #counter <> 1
BEGIN
set #query = #query + (SELECT ',' + ColumnName + ' ' + ColumnDataType FROM #tempStructure where #counter = pk_id)
END
set #counter = #counter + 1
END
END
When you execute the SP, you'll now get a return of the structure of the table you want.
This should hopefully get you moving.
If you want the table CONTENTS included, create yourself a loop for the entries, and append them to the #query parameter.
Remember to delimit the #query, else when you read it later on, you will not be able to restructure your table.
First of all you have to understand that you can't just store the value of a SELECTon a table in simple variable. It has to be TABLE variable which can store the value of a SELECTquery.
Try the below:
select 'name1' name, 12 age
into MyTable
union select 'name2', 15 union
select 'name3', 19
--declaring the table variable and selecting out of it..
declare #QueryResult table(name varchar(30), age int)
insert #QueryResult exec DisplayTable 'MyTable'
select * from #QueryResult
Hope this helps!
I have SQL table that has a large number of columns. For some reason, some columns have empty cells instead of NULL cells. I would like to make all empty cells in all the columns to be NULL.
I know that the way to go for a single column is:
UPDATE your_table SET column = NULL WHERE column = ''
However, I am not sure how to execute a similar logic efficiently for all columns without having to write the column names one by one.
Thanks,
Run the following query:
SELECT 'UPDATE yourtable SET ' + name + ' = NULL WHERE ' + name + ' = '''';'
FROM syscolumns
WHERE id = object_id('yourtable')
AND isnullable = 1;
The output of this query will be a chunk of SQL script like this:
UPDATE yourtable SET column1 = NULL WHERE column1 = '';
UPDATE yourtable SET column2 = NULL WHERE column2 = '';
UPDATE yourtable SET column3 = NULL WHERE column3 = '';
-- etc...
Copy and paste that SQL script into a new query and run it to update all your columns.
You could do a query on syscolumns to get a list of columns, and use the results to construct your query.
select quotename(name) + ' = nullif (' + quotename(name)+ ','''')'
from syscolumns
where id = object_id('yourtable')
Additionally, if you write your query as
update yourtable
set
yourcolumn=nullif(yourcolumn, ''),
yourcolumn2=nullif(yourcolumn2, ''),
...
then you can do it in a single query without a where clause
I actually use Robert N's answer above daily when I'm importing flat file data sets, so I put it into a stored procedure that I could pass a table name to. It just populates a temp table with the update statements, then executes each row in the table.
USE [master]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: LikeableBias
-- Create date: 2016-06-27
-- Description: Finds and NULLs all blank values in table where column allows nulls
-- =============================================
CREATE PROCEDURE [dbo].[sproc_NullBlanks]
#tablename NVARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
--------Insert update statements to temp table for execution
DECLARE #statements TABLE (statement NVARCHAR(MAX))
INSERT INTO #statements
( statement )
SELECT ('UPDATE '+#tablename+' SET [' + name + '] = NULL WHERE ' + name + ' = '''';')
FROM syscolumns
WHERE id = OBJECT_ID(#tablename)
AND isnullable = 1;
--------Open cursor, execute statements, then close cursor
DECLARE #statement NVARCHAR(MAX)
DECLARE cur CURSOR LOCAL FOR
SELECT statement FROM #statements
OPEN cur
FETCH NEXT FROM cur INTO #statement
WHILE ##FETCH_STATUS = 0 BEGIN
EXEC sys.sp_executesql #statement
FETCH NEXT FROM cur INTO #statement
END
CLOSE cur
DEALLOCATE cur
END
GO