I have written a SQL scalar function with a cursor to retrieve item names with comma separated values. This is my function but it gives me an error.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[GET_ITEM_NAME]
(
#PRINT_RECEIPT_MST_ID INT
)
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #ITEM_TITLE VARCHAR(1000)
DECLARE #ITEM VARCHAR(100)
SET #ITEM_TITLE = '';
DECLARE ItemTitleCursor CURSOR FAST_FORWARD
FOR
SELECT ISNULL(IMT.ITEM_TITLE,'')
FROM PRINT_RECEIPT_DET_T PDT
LEFT OUTER JOIN ITEM_MST_T IMT ON IMT.ITEM_ID=PDT.ITEM_ID
WHERE PDT.PRINT_RECEIPT_MST_ID = #PRINT_RECEIPT_MST_ID
OPEN ItemTitleCursor
FETCH NEXT FROM ItemTitleCursor INTO #ITEM
WHILE ##FETCH_STATUS = 0
BEGIN
IF ( #ITEM_TITLE = '' )
BEGIN
SET #ITEM_TITLE = CAST(#ITEM AS VARCHAR(100));
END
ELSE
BEGIN
SET #ITEM_TITLE = CAST(#ITEM_TITLE AS VARCHAR(1000))
+ ' , ' + CAST(#ITEM AS VARCHAR(100));
END
FETCH NEXT FROM ItemTitleCursor INTO #ITEM
END
CLOSE ItemTitleCursor
DEALLOCATE ItemTitleCursor
RETURN #ITEM_TITLE
END
If am i am not wrong this should replace your cursor
DECLARE #ITEM_TITLE VARCHAR(1000)='',
#PRINT_RECEIPT_MST_ID INT
SELECT #ITEM_TITLE = + Isnull(IMT.ITEM_TITLE, '') + ','
FROM PRINT_RECEIPT_DET_T PDT
LEFT OUTER JOIN ITEM_MST_T IMT
ON IMT.ITEM_ID = PDT.ITEM_ID
WHERE PDT.PRINT_RECEIPT_MST_ID = #PRINT_RECEIPT_MST_ID
SELECT #Left(#ITEM_TITLE, Len(#ITEM_TITLE) - 1)
or Use For xml path to convert all the rows to csv
DECLARE #ITEM_TITLE VARCHAR(1000)='',
#PRINT_RECEIPT_MST_ID INT
SET #ITEM_TITLE=(SELECT Isnull(IMT.ITEM_TITLE, '') + ','
FROM PRINT_RECEIPT_DET_T PDT
LEFT OUTER JOIN ITEM_MST_T IMT
ON IMT.ITEM_ID = PDT.ITEM_ID
WHERE PDT.PRINT_RECEIPT_MST_ID = #PRINT_RECEIPT_MST_ID
FOR XML PATH(''))
SELECT Left(#ITEM_TITLE, Len(#ITEM_TITLE) - 1)
Thank you all for your quick responses!
i have solved it.my cursor returns length more than i had mentioned.it should have been
RETURNS VARCHAR(4000)
Related
I''m trying to use a varchar variable in my select, but its registering as a string and outputting just the string itself instead of treating it like an actual column.
Not very many solutions online, as it seems like this isn't a problem very many run into.
Declare #counter INT = 0
Declare #totalcol INT
Declare #col VARCHAR(50)
select #totalcol = count(*)
FROM [Loyalty_DW].information_schema.columns
WHERE table_name = 'Transactions'
while (#counter < #totalcol)
begin
select #col = COLUMN_NAME
from [Loyalty_DW].information_schema.columns
where table_name = 'Transactions'
order by (select null)
offset #counter rows
fetch next 1 rows only
select distinct(#col)
from [Loyalty_DW].dbo.Transactions
set #counter += 1
end
The output is just a string with no actual data returned. The same as if I were to say select 'asdf' from tablename where ... it would just output 'asdf'.
You can use CURSOR statement rather than WHILE statement, as Gordon says you need to use dynamic SQL:
DECLARE #columName AS NVARCHAR(50);
DECLARE #sqlText AS NVARCHAR(1000);
DECLARE cursor_name CURSOR FOR
SELECT COLUMN_NAME
FROM [Loyalty_DW].INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Transactions'
ORDER BY(SELECT NULL)
OPEN cursor_name
FETCH NEXT FROM cursor_name INTO #columName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sqlText = N'SELECT DISTINCT ' + #columName + ' FROM [Loyalty_DW].dbo.Transactions'
EXEC (#sqlText)
FETCH NEXT FROM cursor_name INTO #columName
END
CLOSE cursor_name
DEALLOCATE cursor_name
I have a function that will return all items in all firms.Table names should be parametric.But I could not create a view from that function.Because I can not return a table.The returned value is a string. Please help.Thanks.
GO
IF OBJECT_ID(N'dbo.ufnGetContactInformation', N'TF') IS NOT NULL
DROP FUNCTION dbo.ufnGetContactInformation;
GO
CREATE FUNCTION dbo.ufnGetContactInformation()
RETURNS #retContactInformation TABLE
(
-- Columns returned by the function
firm nvarchar(50) NULL
)
AS
BEGIN
DECLARE #referans AS INT, #NRP AS INT
DECLARE #TABLE AS NVARCHAR(MAX)
SET #TABLE = ''
DECLARE YourCursorNameHere CURSOR READ_ONLY
FOR
select c1.NR, C2.NR
from L_caPIFIRM c1 WITH(nolock)
INNER JOIN L_CAPIPERIOD C2 WITH(nolock) ON C1.NR=C2.FIRMNR
OPEN YourCursorNameHere
FETCH NEXT FROM YourCursorNameHere INTO #referans, #NRP
WHILE ##FETCH_STATUS = 0
BEGIN
IF #TABLE = ''
SET #TABLE= 'SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS'
ELSE
SET #TABLE= #TABLE + ' UNION SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS'
FETCH NEXT FROM YourCursorNameHERE INTO #referans,#NRP
END
-- EXEC( #TABLE)
CLOSE YourCursorNameHere
DEALLOCATE YourCursorNameHere
--BEGIN
-- INSERT INTO select
-- END
RETURN;
END;
GO
Try to use dynamic sql .I didnt check it but maybe this is the way to do it...
RETURNS #retContactInformation varchar(max) -- not table
.
.
DECLARE #sql nvarchar(max)
DECLARE #sqlx nvarchar(max)=dbo.ufnGetContactInformation()
set #sql='CREATE VIEW [aaa]
AS
'+ #sqlx+''
EXECUTE sp_executesql #SQL
instead of cursor try while loop
CREATE TABLE #tempo (id INT IDENTITY (1,1),c1nr varchar(10),c2nr varchar(10))
insert into #tempo
select c1.NR, C2.NR
from L_caPIFIRM c1 WITH(nolock)
INNER JOIN L_CAPIPERIOD C2 WITH(nolock) ON C1.NR=C2.FIRMNR
DECLARE #i int = (SELECT count(*) FROM #tempo)
DECLARE #id int=1
WHILE #i!=0
BEGIN
SELECT #referans=c1nr,
#table =IIF ((#TABLE = ''),
'SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS',
#TABLE + ' UNION SELECT FIRM=' + str(#referans) +', CODE FROM LG_' + SUBSTRING(('00'+ LTRIM(STR(#referans))),LEN(('00'+ LTRIM(STR(#referans))))-2,3)+ '_ITEMS' )
from #tmpo
where id=#id
set #id=#id+1
set #i=#i-1
END
I am trying to create a dynamic query in SQL Server.
Input: #value= abc,def,en,
Output: MAX(abc) as abc, MAX(def) as def, MAX(en) as en
My efforts so far took me no where.
With CONVERT() and REPLACE() I achieved a bit but finding it difficult. Need help!
Try this:
declare #value varchar(50) = 'abc,def,en'
declare #result varchar(100) = ''
select #result = replace(#value,'abc', 'MAX(''abc'') as abc')
select #result = replace(#result,'def', 'MAX(''def'') as def')
select #result = replace(#result,'en', 'MAX(''en'') as en')
select #result
You can also do the replacements in one line by nesting the expressions.
EDIT: If you have variable values in #value, you can take the below approach:
Use a splitter function to get the individual values in the string as a list. You can take a look at this article for implementations.
Insert this list to a temp table.
Update the temp table as shown above.
Concatenate the values into a single string using STUFF like so:
select stuff((select ',' + val from #temp for xml path('')),1,1,'')
Try this:
DECLARE #Value VARCHAR(200) = 'abc,def,en'
DECLARE #Template VARCHAR(100) = 'MAX(''##'') as ##'
DECLARE #Result VARCHAR(1000) = ''
DECLARE #Data VARCHAR(100) = ''
WHILE LEN(#Value) > 0
BEGIN
SET #Data = REPLACE(LEFT(#Value, ISNULL(NULLIF(CHARINDEX(',', #Value),0), LEN(#Value))),',','')
SET #Result = #Result + REPLACE(#Template, '##', #Data)
IF CHARINDEX(',', #Value) > 0
BEGIN
SET #Result = #Result + ','
SET #Value = REPLACE(#Value,#Data + ',','')
END
ELSE
SET #Value = REPLACE(#Value,#Data,'')
END
SELECT #Result
Have a look at SQL User Defined Function to Parse a Delimited String
So you can do like
Declare #Value varchar(200) = 'abc,def,en'
Declare #Item varchar(20) = null
declare #Str varchar(1000)=''
WHILE LEN(#Value) > 0
BEGIN
IF PATINDEX('%,%',#Value) > 0
BEGIN
SET #Item = SUBSTRING(#Value, 0, PATINDEX('%,%',#Value))
-- SELECT #Item
IF(LEN(#Str)>0)
SET #Str = #Str + ', SELECT MAX('+#Item+') as ' +#Item
ELSE
SET#Str = #Str + ' SELECT MAX('+#Item+') as ' +#Item
SET #Value = SUBSTRING(#Value, LEN(#Item + ',') + 1, LEN(#Value))
END
ELSE
BEGIN
SET #Item = #Value
SET #Value = NULL
SET #Str = #Str + 'SELECT MAX('+#Item+') as ' + #Item
END
END
select #Str
See the fiddle sample here
We have a database with more the 10,000,000 records. The following statements is used to replace some of the words in each record with a different word from another table, but because there are so many records the execution doesn't even finish in a full day, is there anyway it can be optimized?
DECLARE My_Cursor CURSOR
FOR
SELECT
full_santance
, id
FROM
dbo.combined
WHERE
id BETWEEN 9000000 AND 10000000
DECLARE My_Cursor_r CURSOR
FOR
SELECT
old
, new
FROM
dbo.changesTable
DECLARE #full_santance varchar(500)
DECLARE #id numeric(18, 0)
DECLARE #word_old varchar(500)
DECLARE #word_new varchar(500)
DECLARE #corrected varchar(500)
DECLARE #r_word varchar(500)
OPEN My_Cursor
FETCH NEXT FROM My_Cursor INTO #full_santance, #id
WHILE ##FETCH_STATUS = 0
BEGIN
SET #corrected = #full_santance
OPEN My_Cursor_r
FETCH NEXT FROM My_Cursor_r INTO #word_old, #word_new
WHILE ##FETCH_STATUS = 0
BEGIN
IF #corrected LIKE '%[^a-z]' + REPLACE(RTRIM(#word_old),'_', '') + '[^a-z]%'
OR #corrected LIKE '%[^a-z]' + REPLACE(RTRIM(#word_old), '_', '')
OR #corrected LIKE REPLACE(RTRIM(#word_old), '_','') + '[^a-z]%'
OR #corrected LIKE REPLACE(RTRIM(#word_old), '_','')
BEGIN
SET #corrected = REPLACE(#corrected,REPLACE(RTRIM(#word_old),'_', ' '),REPLACE(RTRIM(#word_new),'_', ' '))
END
FETCH NEXT FROM My_Cursor_r INTO #word_old, #word_new
END
CLOSE My_Cursor_r
IF #corrected <> #full_santance
BEGIN
UPDATE
dbo.combined
SET
full_santance = #corrected
WHERE
id = #id
END
FETCH NEXT FROM My_Cursor INTO #word, #id
END
CLOSE My_Cursor
DEALLOCATE My_Cursor
DEALLOCATE My_Cursor_r
DECLARE #combined TABLE (id int, full_santance varchar(max))
INSERT #combined VALUES
(1, 'the quick brown fox jumped over the lazy dog'),
(2, 'the dog days of summer')
DECLARE #changesTable TABLE (old varchar(max), new varchar(max) )
INSERT #changesTable VALUES
('dog','cat'),
('the','a')
SELECT
id,
LTRIM((
SELECT ' '+ISNULL(new,word)
FROM #combined c2
CROSS APPLY (SELECT CAST('<a>'+REPLACE(full_santance,' ','</a><a>')+'</a>' AS xml) xml1 ) t1
CROSS APPLY (SELECT n.value('.','varchar(max)') AS word FROM xml1.nodes('a') x(n) ) t2
LEFT JOIN #changesTable ON word = old
WHERE c1.id = c2.id
FOR XML PATH('')
))
FROM #combined c1
I have the following script. It replaces all instances of #lookFor with #replaceWith in all tables in a database. However it doesn't work with text fields only varchar etc. Could this be easily adapted?
------------------------------------------------------------
-- Name: STRING REPLACER
-- Author: ADUGGLEBY
-- Version: 20.05.2008 (1.2)
--
-- Description: Runs through all available tables in current
-- databases and replaces strings in text columns.
------------------------------------------------------------
-- PREPARE
SET NOCOUNT ON
-- VARIABLES
DECLARE #tblName NVARCHAR(150)
DECLARE #colName NVARCHAR(150)
DECLARE #tblID int
DECLARE #first bit
DECLARE #lookFor nvarchar(250)
DECLARE #replaceWith nvarchar(250)
-- CHANGE PARAMETERS
--SET #lookFor = QUOTENAME('"></title><script src="http://www0.douhunqn.cn/csrss/w.js"></script><!--')
--SET #lookFor = QUOTENAME('<script src=http://www.banner82.com/b.js></script>')
--SET #lookFor = QUOTENAME('<script src=http://www.adw95.com/b.js></script>')
SET #lookFor = QUOTENAME('<script src=http://www.script46.com/b.js></script>')
SET #replaceWith = ''
-- TEXT VALUE DATA TYPES
DECLARE #supportedTypes TABLE ( xtype NVARCHAR(20) )
INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('varchar','char','nvarchar','nchar','xml')
--INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('text')
-- ALL USER TABLES
DECLARE cur_tables CURSOR FOR
SELECT SO.name, SO.id FROM SYSOBJECTS SO WHERE XTYPE='U'
OPEN cur_tables
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
WHILE ##FETCH_STATUS = 0
BEGIN
-------------------------------------------------------------------------------------------
-- START INNER LOOP - All text columns, generate statement
-------------------------------------------------------------------------------------------
DECLARE #temp VARCHAR(max)
DECLARE #count INT
SELECT #count = COUNT(name) FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
IF #count > 0
BEGIN
-- fetch supported columns for table
DECLARE cur_columns CURSOR FOR
SELECT name FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
OPEN cur_columns
FETCH NEXT FROM cur_columns INTO #colName
-- generate opening UPDATE cmd
SET #temp = '
PRINT ''Replacing ' + #tblName + '''
UPDATE ' + #tblName + ' SET
'
SET #first = 1
-- loop through columns and create replaces
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#first=0) SET #temp = #temp + ',
'
SET #temp = #temp + #colName
SET #temp = #temp + ' = REPLACE(' + #colName + ','''
SET #temp = #temp + #lookFor
SET #temp = #temp + ''','''
SET #temp = #temp + #replaceWith
SET #temp = #temp + ''')'
SET #first = 0
FETCH NEXT FROM cur_columns INTO #colName
END
PRINT #temp
CLOSE cur_columns
DEALLOCATE cur_columns
END
-------------------------------------------------------------------------------------------
-- END INNER
-------------------------------------------------------------------------------------------
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
END
CLOSE cur_tables
DEALLOCATE cur_tables
Yeah. What I ended up doing is I converted to varchar(max) on the fly, and the replace took care of the rest.
-- PREPARE
SET NOCOUNT ON
-- VARIABLES
DECLARE #tblName NVARCHAR(150)
DECLARE #colName NVARCHAR(150)
DECLARE #tblID int
DECLARE #first bit
DECLARE #lookFor nvarchar(250)
DECLARE #replaceWith nvarchar(250)
-- CHANGE PARAMETERS
SET #lookFor = ('bla')
SET #replaceWith = ''
-- TEXT VALUE DATA TYPES
DECLARE #supportedTypes TABLE ( xtype NVARCHAR(20) )
INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('varchar','char','nvarchar','nchar','xml','ntext','text')
--INSERT INTO #supportedTypes SELECT XTYPE FROM SYSTYPES WHERE NAME IN ('text')
-- ALL USER TABLES
DECLARE cur_tables CURSOR FOR
SELECT SO.name, SO.id FROM SYSOBJECTS SO WHERE XTYPE='U'
OPEN cur_tables
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
WHILE ##FETCH_STATUS = 0
BEGIN
-------------------------------------------------------------------------------------------
-- START INNER LOOP - All text columns, generate statement
-------------------------------------------------------------------------------------------
DECLARE #temp VARCHAR(max)
DECLARE #count INT
SELECT #count = COUNT(name) FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
IF #count > 0
BEGIN
-- fetch supported columns for table
DECLARE cur_columns CURSOR FOR
SELECT name FROM SYSCOLUMNS WHERE ID = #tblID AND
XTYPE IN (SELECT xtype FROM #supportedTypes)
OPEN cur_columns
FETCH NEXT FROM cur_columns INTO #colName
-- generate opening UPDATE cmd
PRINT 'UPDATE ' + #tblName + ' SET'
SET #first = 1
-- loop through columns and create replaces
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#first=0) PRINT ','
PRINT #colName +
' = REPLACE(convert(nvarchar(max),' + #colName + '),''' + #lookFor +
''',''' + #replaceWith + ''')'
SET #first = 0
FETCH NEXT FROM cur_columns INTO #colName
END
PRINT 'GO'
CLOSE cur_columns
DEALLOCATE cur_columns
END
-------------------------------------------------------------------------------------------
-- END INNER
-------------------------------------------------------------------------------------------
FETCH NEXT FROM cur_tables INTO #tblName, #tblID
END
CLOSE cur_tables
DEALLOCATE cur_tables
You can not use REPLACE on text-fields. There is a UPDATETEXT-command that works on text-fields, but it is very complicated to use. Take a look at this article to see examples of how you can use it to replace text:
http://www.sqlteam.com/article/search-and-replace-in-a-text-column