This is the table creation and insertion query
If not exists(select * from sysobjects where name='hrs')
Create table hrs(hr int)
declare #cnt int =1
while #cnt <= 12
begin
insert into hrs values(#cnt)
set #cnt=#cnt+1
end
The above code gives the output like
but I just want that
declare #cnt1 int = 1
while #cnt1<=12
begin
EXEC('select he'+#cnt1+' = case when hr = 1 then '+#cnt1+' end from hrs')
set #cnt1=#cnt1+1
end
The above code returns the 12 different table but i just want the all records in one table (without creating any new table).
So, how can i do this?
Please help me.
Thanks.
Here the all column are created dynamically through loop
Here are the full query
declare #s varchar(MAX)=''
declare #j int = 1
while #j<=12
begin
if #j = 12
Set #s = #s+'he'+convert(varchar,#j)+'=MAX(case when hr='+convert(varchar,#j)+' then '+convert(varchar,#j)+' end)'
else
set #s = #s+'he'+convert(varchar,#j)+'=MAX(case when hr='+convert(varchar,#j)+' then '+convert(varchar,#j)+' end),'
set #j=#j+1
end
set #s = 'select '+#s+' from hrs'
exec(#s)
Your query doesn't make a lot of sense, but you can build a list of columns and then exec that:
declare #columns nvarchar(max)
declare #cnt int = 1
while #cnt <= 12
begin
set #columns = isnull(#columns + ', ', '') + 'He' + cast(#cnt as nvarchar) +
' = sum(case when hr = ' + cast(#cnt as nvarchar) + ' then hr end)'
end
declare #sql nvarchar(max) = 'select ' + #columns ' + from hr'
exec (#sql)
Related
I have this little statement to extract the list of the column of a table:
DECLARE #j INT = 1
DECLARE #verifyColumn INT = #pkcounter --it count the number of column of the table
WHILE #j <> #verifyColumn +1
BEGIN
set #multipleColumn = #multipleColumn+ (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMN WHERE TABLE_SCHEMA = #schema_name AND TABLE_NAME = #table_source AND ORDINAL_POSITION = #k) + ','
SET #j = #j + 1
set #k = #k + 1
SET #pkcounter = #pkcounter - 1;
END
PRINT 'multipleColumn: ' + #multipleColumn
Now i need to use the variable with the list of the column in an insert statement, a sort of:
INSERT INTO table_name_target (#multipleColumn) SELECT a,b FROM table_name_source
It's possible to do that?
Thank you all
No, you must use sp_executesql. Like this:
DECLARE #multipleColumn VARCHAR(1000) = NULL, -- Must be null
#schema_name NVARCHAR(128)='dbo',
#table_source NVARCHAR(128)='<src>',
#table_target NVARCHAR(128)='<dest>',
#stmt NVARCHAR(MAX);
SELECT #multipleColumn = COALESCE(#multipleColumn+', ', '') + QUOTENAME(c.COLUMN_NAME)
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE
c.TABLE_SCHEMA = #schema_name
AND c.TABLE_NAME = #table_source;
SET #stmt=CONCAT('INSERT INTO ', QUOTENAME(#table_target),' (', #multipleColumn, ')
SELECT ', #multipleColumn, ' FROM ', QUOTENAME(#table_source), ';');
EXEC sp_executesql #stmt;
And, as always, be careful of sql injection when using sp_executesql!
I have a procedure that generates dynamic SQL that creates an insert into statement while querying an excel spreadsheet.
The resulting print from the messages screen can be pasted into an ssms window and executes. When I try to execute the SQL from within the stored procedure I get a syntax error as follows:
'SELECT * into TestClient FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0', 'Excel 12.0;HDR=YES;Database=G:\CustomerETL\Employee\PendingETL\ETLEmployeexls.xls;', [Sheet1$])'
Msg 102, Level 15, State 1, Line 15
Incorrect syntax near 'SELECT * into TestClient FROM OPENROWSET('.
Below is the entire stored procedure. I know the problem is in the ticks (within the SET blocks that create the dynamic SQL I just can't figure out where the missing ticks are.
Here is the proc:
USE [ETL]
GO
/****** Object: StoredProcedure [dbo].[ImportExcelSheetForCustomerEmployeeUpdate2] Script Date: 12/19/2017 4:03:05 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ImportExcelSheetForCustomerEmployeeUpdate2](#BatchID int)
as
--EXEC ImportExcelSheetForCustomerEmployeeUpdate 2
/* -- TRUNCATE TABLE FilesToImport
UPDATE FilesToImport
SET StatusID = 1
*/
-- Jeffery Williams
-- 12/18/2017
DECLARE #FileID int
,#ETLFilename varchar(250)
,#ClientName varchar(100)
,#FileType varchar(5)
,#ColumnCount int
,#RowsToETL int
,#StatusID int
,#Processed bit = 0
,#Count int
,#SQL nvarchar(4000)
,#Sheetname varchar(50) = '[Sheet1$]'
,#CMDSQL as varchar(4000)
,#SQLCmd NVARCHAR(MAX)
SELECT *
FROM FilesToImport
BEGIN
SELECT #Count = count(*)
FROM FilesToImport
WHERE BatchID = #BatchID
AND StatusID = 1
END
PRINT 'Count of records to process: ' + cast(#Count as varchar)
WHILE #Count > 0
BEGIN
BEGIN
SELECT TOP 1 #FileID = FileID, #ETLFilename = ETLFilename, #ClientName = ClientName
,#FileType = FileType, #ColumnCount = ColumnCount, #RowsToETL = RowsToETL
FROM FilesToImport
WHERE StatusID = 1
AND BatchID = #BatchID
END
-- Rename the file
set #CMDSQL = 'rename G:\CustomerETL\Employee\PendingETL\' + #ETLFilename + ' ETLEmployeexls.xls'
exec master..xp_cmdshell #CMDSQL
--PRINT cast(#cmdsql as varchar(4000))
-- Ciode below generates our select. Need to add an INTO clause and create a staging table for each import. Prior to this step we need to rename the file.
SET #SQL = ''''
SET #SQL = #SQL + 'SELECT * into ' + coalesce(#ClientName, 'TestClient') + ' FROM OPENROWSET('
SET #SQL = #SQL + ''''
SET #SQL = #SQL + '''' + 'Microsoft.ACE.OLEDB.12.0' + '''' --+ ', '
-- Excel 12.0;HDR=NO;Database=g:\UnZip\ImportSampleXLSX.xlsx;' + ''
SET #SQL = #SQL + '''' + ', '
SET #SQL = #SQL + '''' + '''Excel 12.0;HDR=YES;Database=G:\CustomerETL\Employee\PendingETL\ETLEmployeexls.xls;''' + '''' + ', ' + #Sheetname + ')'
SET #SQL = #SQL + ''''
PRINT cast(#SQL as varchar(8000))
EXEC sp_executesql #SQL
set #CMDSQL = 'rename G:\CustomerETL\Employee\PendingETL\ETLEmployeexls.xls ' + #ETLFilename
exec master..xp_cmdshell #CMDSQL
UPDATE FilesToImport
SET StatusID = 2
WHERE FileID = #FileID
/* -- TRUNCATE TABLE FilesToImport
UPDATE FilesToImport
SET StatusID = 1
*/
SET #Count = (#Count - 1)
CONTINUE
END
I am posting this as an answer but it should be comment. When I tried adding this as a comment StackOveflow kept thinking that I was trying to add #count as an email target.
In your code:
WHILE #Count > 0
BEGIN
BEGIN
SELECT TOP 1 #FileID = FileID, #ETLFilename = ETLFilename, #ClientName = ClientName
,#FileType = FileType, #ColumnCount = ColumnCount, #RowsToETL = RowsToETL
FROM FilesToImport
WHERE StatusID = 1
AND BatchID = #BatchID
END
you are not updating the value of #count. This will either never loop or loop forever. You probably want to add a statement (right before the end) such as this:
Set #count= ##rowcount;
Ben
I am having trouble converting an UDF into a stored procedure.
Here is what I've got: this is the stored procedure that calls the function (I am using it to search for and remove all UNICODE characters that are not between 32 and 126):
ALTER PROCEDURE [dbo].[spRemoveUNICODE]
#FieldList varchar(250) = '',
#Multiple int = 0,
#TableName varchar(100) = ''
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL VARCHAR(MAX), #counter INT = 0
IF #Multiple > 0
BEGIN
DECLARE #Field VARCHAR(100)
SELECT splitdata
INTO #TempValue
FROM dbo.fnSplitString(#FieldList,',')
WHILE (SELECT COUNT(*) FROM #TempValue) >= 1
BEGIN
DECLARE #Column VARCHAR(100) = (SELECT TOP 1 splitdata FROM #TempValue)
SET #SQL = 'UPDATE ' + #TableName + ' SET ' + #Column + ' = dbo.RemoveNonASCII(' + #Column + ')'
EXEC (#SQL)
--print #SQL
SET #counter = #counter + 1
PRINT #column + ' was checked for ' + #counter + ' rows.'
DELETE FROM #TempValue
WHERE splitdata = #Column
END
END
ELSE IF #Multiple = 0
BEGIN
SET #SQL = 'UPDATE ' + #TableName + ' SET ' + #FieldList + ' = dbo.RemoveNonASCII(' + #FieldList + ')'
EXEC (#SQL)
--print #SQL
SET #counter = #counter + 1
PRINT #column + ' was checked for ' + #counter + ' rows.'
END
END
And here is the UDF that I created to help with the update (RemoveNonASCII):
ALTER FUNCTION [dbo].[RemoveNonASCII]
(#nstring nvarchar(max))
RETURNS varchar(max)
AS
BEGIN
-- Variables
DECLARE #Result varchar(max) = '',#nchar nvarchar(1), #position int
-- T-SQL statements to compute the return value
set #position = 1
while #position <= LEN(#nstring)
BEGIN
set #nchar = SUBSTRING(#nstring, #position, 1)
if UNICODE(#nchar) between 32 and 127
set #Result = #Result + #nchar
set #position = #position + 1
set #Result = REPLACE(#Result,'))','')
set #Result = REPLACE(#Result,'?','')
END
if (#Result = '')
set #Result = null
-- Return the result
RETURN #Result
END
I've been trying to convert it into a stored procedure. I want to track how many rows actually get updated when this is run. Right now it just says that all rows, however many I run this on, are updated. I want to know if say only half of them had bad characters. The stored procedure is already set up so that it tells me which column it is looking at, I want to include how many rows were updated. Here is what I've tried so far:
DECLARE #Result varchar(max) = '',#nchar nvarchar(1), #position int, #nstring nvarchar(max), #counter int = 0, #CountRows int = 0, #Length int
--select Notes from #Temp where Notes is not null order by Notes OFFSET #counter ROWS FETCH NEXT 1 ROWS ONLY
set #nstring = (select Notes from #Temp where Notes is not null order by Notes OFFSET #counter ROWS FETCH NEXT 1 ROWS ONLY)
set #Length = LEN(#nstring)
if #Length = 0 set #Length = 1
-- Add the T-SQL statements to compute the return value here
set #position = 1
while #position <= #Length
BEGIN
print #counter
print #CountRows
select #nstring
set #nchar = SUBSTRING(#nstring, #position, 1)
if UNICODE(#nchar) between 32 and 127
begin
print unicode(#nchar)
set #Result = #Result + #nchar
set #counter = #counter + 1
end
if UNICODE(#nchar) not between 32 and 127
begin
set #CountRows = #CountRows + 1
end
set #position = #position + 1
END
print 'Rows found with invalid UNICODE: ' + convert(varchar,#CountRows)
Right now I'm purposely creating a temp table and adding a bunch of notes and then adding in a bunch of invalid characters.
I created a list of 700+ Notes and then updated 2 of them with some invalid characters (outside the 32 - 127). There are a few that are null and a few that are not null, but that doesn't have anything in them. What happens is that I get 0 updates.
Rows found with invalid UNICODE: 0
Though it does see that the UNICODE for the one that it pulls is 32.
Obviously I'm missing something I just don't see what it is.
Here is a set based solution to handle your bulk replacements. Instead of a slow scalar function this is utilizing an inline table valued function. These are far faster than their scalar ancestors. I am using a tally table here. I keep this as a view on my system like this.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
If you are interested about tally tables here is an excellent article on the topic. http://www.sqlservercentral.com/articles/T-SQL/62867/
create function RemoveNonASCII
(
#SearchVal nvarchar(max)
) returns table as
RETURN
with MyValues as
(
select substring(#SearchVal, N, 1) as MyChar
, t.N
from cteTally t
where N <= len(#SearchVal)
and UNICODE(substring(#SearchVal, N, 1)) between 32 and 127
)
select distinct MyResult = STUFF((select MyChar + ''
from MyValues mv2
order by mv2.N
--for xml path('')), 1, 0, '')
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'), 1, 0, '')
from MyValues mv
;
Now instead of being forced to call this every single row you can utilize cross apply. The performance benefit of just this portion of your original question should be pretty huge.
I also eluded to your string splitter also being a potential performance issue. Here is an excellent article with a number of very fast set based string splitters. http://sqlperformance.com/2012/07/t-sql-queries/split-strings
The last step here would be eliminate the first loop in your procedure. This can be done also but I am not entirely certain what your code is doing there. I will look closer and see what I can find out. In the meantime parse through this and feel free to ask questions about any parts you don't understand.
Here is what I've got working based on the great help from Sean Lange:
How I call the Stored Procedure:
exec spRemoveUNICODE #FieldList='Notes,Notes2,Notes3,Notes4,Notes5',#Multiple=1,#TableName='#Temp'
The #Temp table is created:
create table #Temp (ID int,Notes nvarchar(Max),Notes2 nvarchar(max),Notes3 nvarchar(max),Notes4 nvarchar(max),Notes5 nvarchar(max))
Then I fill it with comments from 5 fields from a couple of different tables that range in length from NULL to blank (but not null) to 5000 characters.
I then insert some random characters like this:
update #Temp
set Notes2 = SUBSTRING(Notes2,1,LEN(Notes2)/2) + N'㹊潮Ņࢹᖈư㹨ƶ槹鎤⻄ƺ綐ڌ⸀ƺ삸)䀤ƍ샄)Ņᛡ鎤ꗘᖃᒨ쬵Ğᘍ鎤ᐜᏰ>֔υ赸Ƹ쳰డ촜)鉀촜)쮜)Ἡ屰山舰霡ࣆ 耏Аం畠Ư놐ᓜતᏛ֔Ꮫ֨Ꮫᓜƒ 邰厰ఆ邰드)抉鎤듄)繟Ĺ띨)ࢹ䮸ࣉࢹ䮸ࣉ샰)ԌƏ
I have 64 columns and I am trying to automate the loop process. The loop runs but it shows 0 affected rows. If I update the table, column by column, it works.
Any idea why its showing 0 affected rows and what can be done ?
update temp set col1 = 'C' where col1 IS Null; -- works (276 rows affected)--
declare #count as int;
declare #name as varchar(max);
set #count = 2;
while #count < (SELECT Count(*) FROM INFORMATION_SCHEMA.Columns where TABLE_NAME = 'temp')+1
Begin
Set #name = (select name from (select colorder, name from (SELECT *
FROM syscolumns WHERE id=OBJECT_ID('temp')) colnames) as cl where colorder = #count)
Print #name
update temp set #name = 'C' where #name IS Null;
SET #count = #count + 1;
END;
You need to use dynamic sql to update the different columns during runtime as below.
Note: I just added/modified the dynamic sql part.
declare #count as int;
declare #name as varchar(max)
declare #sql nvarchar (1000)
set #count = 2
while #count < (SELECT Count(*) FROM INFORMATION_SCHEMA.Columns where TABLE_NAME = 'temp')+1
Begin
Set #name = (select name from (select colorder, name from (SELECT *
FROM syscolumns WHERE id=OBJECT_ID('temp')) colnames) as cl where colorder = #count)
Print #name
set #sql = N'update temp set ' + #name + '= ''C'' where ' + #name + ' is null ';
exec sp_executesql #sql
SET #count = #count + 1
END;
I have a table that stores SQL queries. I retrieve a query according to a condition and store it in a variable:
---------------------------------------------------------
ID | Query
---------------------------------------------------------
1 | 'Select Id,Name from Student'
2 | 'Select Id,Name,Father_Name from Student'
3 | 'Select Id,Name,Email from Student_Detail'
....
---------------------------------------------------------
For example, a variable #sql might have the first query from above:
Declare #sql nvarchar(500)
set #sql = 'Select Id,Name from Student'
I execute this query using:
Exec(#sql)
The problem is, how do I know which columns are used in that query? I'm trying to achieve something like what ColdFusion does with query.ColumnList, which returns the column list used in that query.
Try this:
SELECT SUBSTRING(query,8,CHARINDEX('from',query)-9) AS ColumnList
FROM YourTable
this is a variant of mehdi lotfi solution but is equally weak in the sense that only gives you whatever is in between the select and from statements so if you have aliases or calculations or 'case' statements it will not work properly; it does work if the column list is straight forward comma separated columns:
SELECT LEFT(REPLACE(#SQL,'Select ',''), CHARINDEX(' from',REPLACE(#SQL,'Select ',''))) AS ColumnList
Finally i managed to solve at my own using this solution.
Declare #sql varchar(1000)
Declare #valueList nvarchar(500)
Declare #tbl Table(Name varchar(100))
Declare #selectPos Int
Declare #fromPos Int
Declare #len Int
Declare #pos Int
Declare #prevpos Int
Declare #Delimeter varchar(2)
set #sql = 'Select Id,Name,Father_Name from Student'
set #selectPos = CHARINDEX('Select ', #sql, 1)+7
set #fromPos = CHARINDEX('From ', #sql, 1)
set #len = #fromPos - #selectPos
set #valueList = SUBSTRING(#sql, #selectPos, #len)
set #Delimeter = ', '
set #pos = 1
set #prevpos = 0
while #pos > 0
Begin
set #pos = charIndex(#Delimeter, #valueList, #pos)
If #pos = 0
Begin
Insert into #tbl
Select SUBSTRING(#valueList,#prevPos + 1,LEN(#valueList) - #prevpos)
Break;
End
Insert into #tbl
Select SUBSTRING(#valueList,#prevPos + 1,#pos-#prevpos - 1)
set #prevpos = #pos
set #pos = #pos + 1
End
select * from #tbl