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
From this answer: Is there a way to loop through a table variable in TSQL without using a cursor?
I'm using the method
WHILE EXISTS(SELECT * FROM #Temp)
The problem is that it's outputting multiple tables, if possible I'd like to output as a single table.
Declare #Id int
WHILE EXISTS(SELECT * FROM #Temp)
Begin
Select Top 1 #Id = Id From #Temp
--Do some processing here
Delete #Temp Where Id = #Id
End
So right now it outputs this:
x y
-- --
1 a
x y
-- --
1 b
But I'd like it to output this:
x y
-- --
1 a
2 b
What I'm trying to achieve, I have this in a field:
1234,1432,1235
I have a process that splits the field into records(it works with sql server 2000):
DECLARE #String VARCHAR(100)
SELECT #String = str FROM field --with the 1234,1432,1235
SELECT SUBSTRING(',' + #String + ',', Number + 1,
CHARINDEX(',', ',' + #String + ',', Number + 1) - Number -1)AS str
INTO #temp
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + #String + ',') - 1
AND SUBSTRING(',' + #String + ',', Number, 1) = ','
GO
So now, #temp has:
str
---
1234
1432
1235
So I need to go through each record to query the information I need.
And I'd like it to output something like this:
str name age
--- ---- ---
1234 Bob 23
1432 Jay 41
1235 Tim 12
The current While loop outputs it like this, which I don't want:
str name age
--- ---- ---
1234 Bob 23
str name age
--- ---- ---
1432 Jay 41
str name age
--- ---- ---
1235 Tim 12
Final Working Result:
SET NOCOUNT ON;
DECLARE #String VARCHAR(1000);
SELECT #String = Tnn FROM (SELECT
CO.USER_2 AS Tnn
FROM
[VMFG].[dbo].[CUSTOMER_ORDER] AS CO
LEFT JOIN DBO.Tnn_Header AS Tnn ON Tnn.TnnNumber = CO.USER_2 AND Tnn.StatusID = '5' WHERE CO.ID = 'ORDERID') AS Place --with the 1234,1432,1235
DECLARE #Id nvarchar(50),
#Discount nvarchar(50),
#Spin nvarchar(50),
#Commission_Hmm nvarchar(50),
#Commission nvarchar(50),
#TnnID nvarchar(50);
DECLARE #Output TABLE (
TnnNumber nvarchar(50),
Discount nvarchar(50),
Spin nvarchar(50),
Commission_Hmm nvarchar(50),
Commission nvarchar(50),
TnnID nvarchar(50));
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT SUBSTRING(',' + #String + ',', Number + 1,
CHARINDEX(',', ',' + #String + ',', Number + 1) - Number -1) AS [ID]
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + #String + ',') - 1
AND SUBSTRING(',' + #String + ',', Number, 1) = ',';
OPEN crs;
FETCH NEXT
FROM crs
INTO #Id;
WHILE (##FETCH_STATUS = 0)
BEGIN
-- do some processing..
SELECT
#Id = TH.TnnNumber,
#Discount = CASE WHEN COUNT(DISTINCT TL.DiscountCodeID) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX(DC.Value) AS VARCHAR(60)) END,
#Spin = CASE WHEN TS.SpinID > 4 THEN 'Has Specifics, View Tnn' ELSE TS.Value END,
#Commission_Hmm = CASE WHEN COUNT(DISTINCT TL.Commission_Hmm) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX( ISNULL(str(TL.Commission_Hmm,12),'Default Comm')) AS VARCHAR(60)) END,
#Commission = CASE WHEN COUNT(DISTINCT TL.Commission) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX(ISNULL(str(TL.Commission,12),'Default Comm')) AS VARCHAR(60)) END,
#TnnID = TL.TnnID
FROM DBO.Tnn_Header AS TH
LEFT JOIN DBO.Tnn_LINE AS TL ON TH.TnnID = TL.TnnID
LEFT JOIN DBO.Tnn_Spin AS TS ON TH.SpinID = TS.SpinID
LEFT JOIN DBO.Tnn_DiscountCode AS DC ON TL.DiscountCodeID = DC.DiscountCodeID
WHERE TnnNumber = #id
GROUP BY
TH.TnnNumber,
TS.SpinID,
TS.Value,
TL.TnnID
-- end do some processing..
INSERT INTO #Output (TnnNumber, Discount, Spin, Commission_Hmm, Commission, TnnID)
VALUES (#Id, #Discount, #Spin, #Commission_Hmm, #Commission, #TnnID);
FETCH NEXT
FROM crs
INTO #Id;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT TnnNumber, Discount, Spin, Commission_Hmm, Commission, TnnID
FROM #Output;
You are wasting your time and energy following such bad advice. If you absolutely must (extra emphasis on the must) take a row-by-row approach (CURSOR or WHILE loop), then you are better off with a CURSOR. It is a built-in construct that is more efficient, and less error-prone. You just need to use the right options, such as making it STATIC, LOCAL, READ_ONLY, and FORWARD_ONLY. You don't need STATIC if the cursor query is only hitting temporary tables and/or table variables.
People will argue with this and say that "you must avoid cursors at all cost!", but they haven't done the tests to see that such a popular notion is really just a myth. And if they have done tests that appear to confirm it, then they haven't set the appropriate options, mostly STATIC, which dumps the result of the cursor query into a temp table. Without this option, fetching new rows will re-check the base tables to make sure that they still exist, and that is where the performance hit is (the I/O plus the locking). And that is also why you typically don't need the STATIC option when querying only temporary tables and/or table variables. What do I mean by "re-checking"? Just look at the documentation for ##FETCH_STATUS. The return values don't just cover "success" (0) and "no more rows" (-1): there is a return value, (-2), that means "The row fetched is missing".
SET NOCOUNT ON;
DECLARE #Id INT,
#Name sysname,
#Type VARCHAR(5);
-- the Table Variable replaces #Temp2 in the original query
DECLARE #Output TABLE (Id INT NOT NULL, Name sysname, [Type] VARCHAR(5));
-- the CURSOR replaces #Temp in the original query
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT [object_id], name, [type]
FROM sys.objects -- dbo.sysobjects for SQL 2000 -- ATable in the original query
ORDER BY [object_id] ASC;
OPEN crs;
FETCH NEXT
FROM crs
INTO #Id, #Name, #Type;
WHILE (##FETCH_STATUS = 0)
BEGIN
INSERT INTO #Output (Id, Name, [Type])
VALUES (#Id, #Name, #Type);
-- do some processing..
FETCH NEXT -- replaces the DELETE and re-SELECT in the original query
FROM crs
INTO #Id, #Name, #Type;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT Id, Name, [Type]
FROM #Output;
UPDATE
Given the iteration is being done over a query that splits a CSV of INTs, the resulting query would look similar to the following:
SET NOCOUNT ON;
DECLARE #String VARCHAR(1000);
SELECT #String = str FROM [Table]; --with the 1234,1432,1235
DECLARE #Id INT,
#Name NVARCHAR(50),
#Age TINYINT;
DECLARE #Output TABLE (Id INT NOT NULL, Name NVARCHAR(50), Age TINYINT);
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT SUBSTRING(',' + #String + ',', Number + 1,
CHARINDEX(',', ',' + #String + ',', Number + 1) - Number -1) AS [ID]
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + #String + ',') - 1
AND SUBSTRING(',' + #String + ',', Number, 1) = ',';
OPEN crs;
FETCH NEXT
FROM crs
INTO #Id;
WHILE (##FETCH_STATUS = 0)
BEGIN
-- do some processing..
-- Logic to set value of #Name
-- Logic to set value of #Age
INSERT INTO #Output (Id, Name, Age)
VALUES (#Id, #Name, #Age);
FETCH NEXT
FROM crs
INTO #Id;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT Id, Name, Age
FROM #Output;
your query has syntax error but I tried below query and worked fine
-- this is only to populate my data table
Select object_id Id, name Into #Temp From sys.tables
select * into #temp2 from #Temp where 1=2
Declare #Id int
WHILE EXISTS(SELECT * FROM #Temp)
Begin
Select Top 1 #Id = Id
From #Temp
ORDER BY Id -- this order is important
-- use insert...into, NOT select...into
insert into #temp2
select *
from #Temp
where Id = #Id
Delete #Temp Where Id = #Id
End
BTW, you can not have SELECT...INTO inside a loop, as the 2nd iteration will raise error.
You need to create #temp2, out side the loop and use INSERT...INTO instead of SELECT...INTO
i have used trigger for recalculation multiple data.
in trigger i have used cursor.in cursor i have used another sp's which is contain 1 more cursor.
so it's very slow for calculation of only 80 rows data. and it's take 20 second to calculate the data. so please give me any suggestion or solution for it quickly.
thanks
here is my trigger
/****** Object: Trigger [dbo].[tr_TransactionJournal_InsteadOfDelete] Script Date: 12/23/2015 3:13:09 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[tr_TransactionJournal_InsteadOfDelete]
ON [dbo].[TransactionJournal]
INSTEAD OF DELETE
AS
SET NOCOUNT ON;
DECLARE
#TxNum INT,
#TxDate DATE
/*
declare #TempDetailTable to figure out which ChartAcctCode & Item needs to be recalculated
*/
DECLARE #TempDetailTable TABLE (
ChartAcctCode NVARCHAR(10),
ItemCode NVARCHAR(10)
)
DECLARE #MyCursor_1 CURSOR
SET #MyCursor_1 = CURSOR FAST_FORWARD
FOR
SELECT TxNum,TxDate FROM DELETED
OPEN #MyCursor_1
FETCH NEXT FROM #MyCursor_1
INTO #TxNum,#TxDate
WHILE ##fetch_status = 0 -- This is executed as long as the previous fetch succeeds.
BEGIN
------------------
BEGIN TRANSACTION
INSERT INTO #TempDetailTable
SELECT DISTINCT tjd.ChartAcctCode, tjd.ItemCode
FROM dbo.TransactionJournalDetail AS tjd
WHERE (tjd.TxNum = #TxNum)-- AND tjd.ItemCode IS NOT NULL
COMMIT TRANSACTION
/*
Actually deleting FROM TxJournal & TxJournalDetail
*/
DELETE FROM TransactionJournal WHERE TxNum=#TxNum
/*
Start Recalculating...
*/
DECLARE #MyChartAcctCode NVARCHAR(10)
DECLARE #MyItemCode NVARCHAR(10)
DECLARE #MyCursor CURSOR
/*
1st Loop to RecalcQAV if Detail has any ChartAcctCode = "AINV"
*/
SET #MyCursor = CURSOR FAST_FORWARD
FOR
SELECT ItemCode FROM #TempDetailTable WHERE ChartAcctCode = 'INV'
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #MyItemCode
WHILE ##fetch_status = 0
BEGIN
EXEC RecalcQAV #MyItemCode, #TxDate;
FETCH NEXT FROM #MyCursor
INTO #MyItemCode
END
/*
2nd Loop to RecalcAcctBalance if Detail has any ChartAcctCode = "AINV"
*/
SET #MyCursor = CURSOR FAST_FORWARD
FOR
SELECT ChartAcctCode FROM #TempDetailTable
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #MyChartAcctCode
WHILE ##fetch_status = 0
BEGIN
EXEC RecalcAcctBalance #MyChartAcctCode, #TxDate;
FETCH NEXT FROM #MyCursor
INTO #MyChartAcctCode
END
CLOSE #MyCursor;
DEALLOCATE #MyCursor;
------------------
FETCH NEXT FROM #MyCursor_1
INTO #TxNum,#TxDate
END
CLOSE #MyCursor_1;
DEALLOCATE #MyCursor_1;
ALTER PROCEDURE [dbo].[RecalcAcctBalance]
(
#ChartAcctCode NVARCHAR(10),
#BeginDate DATE
)
AS BEGIN
SET NOCOUNT ON;
DECLARE #LastDate DATE
SELECT #LastDate = MAX(tj.TxDate)
FROM dbo.TransactionJournal AS tj
JOIN dbo.TransactionJournalDetail AS tjd ON tj.TxNum = tjd.TxNum
WHERE tj.TxDate < #BeginDate
AND tjd.ChartAcctCode = #ChartAcctCode
DECLARE #MyTable TABLE (
AutoId INT IDENTITY(1,1) PRIMARY KEY,
TxDate DATE,
TxDetailId INT
)
INSERT INTO #MyTable (TxDate, TxDetailId)
SELECT tj.TxDate, tjd.AutoId
FROM dbo.TransactionJournal AS tj
JOIN dbo.TransactionJournalDetail AS tjd ON tj.TxNum = tjd.TxNum
WHERE tj.TxDate >= ISNULL(#LastDate, #BeginDate)
AND tjd.ChartAcctCode = #ChartAcctCode
ORDER BY tj.TxDate, tj.SourceDocOrder, tjd.AutoId
DECLARE
#LastDateMaxId INT,
#TxDetailId INT,
#LChartAcctBalance MONEY = 0
IF #LastDate IS NOT NULL
BEGIN
SELECT #TxDetailId = TxDetailId
FROM (
SELECT TxDetailId, RowNum = ROW_NUMBER() OVER (ORDER BY AutoId DESC)
FROM #MyTable
WHERE TxDate = #LastDate
) t
WHERE t.RowNum = 1
SELECT #LChartAcctBalance = ISNULL(ChartAcctBalance, 0)
FROM dbo.TransactionJournalDetail
WHERE AutoId = #TxDetailId
END
;WITH cte AS
(
SELECT *, val = #LChartAcctBalance + SUM(Amount) OVER (ORDER BY AutoId)
FROM dbo.TransactionJournalDetail
WHERE AutoId IN (
SELECT TxDetailId
FROM #MyTable
WHERE TxDate >= #BeginDate
)
)
UPDATE cte
SET ChartAcctBalance = val
END
the second sp is too large for refactoring... anyway the main problem - business logic with processing by item...
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
Input like 111111 and 101,102,103,104
I want to check whether user has access on this requests or not...
I tried a cursor as shown, but I get this error:
Conversion failed when converting the varchar value '101,102,103,104' to data type int.
Code:
ALTER PROCEDURE [dbo].[ValidateRqstId]
#UserID VARCHAR(50),
#RsqtIDs VARCHAR(300)
AS
BEGIN
Declare #RqstId int
Declare #Result int
Declare #UserIDToCheck VARCHAR(50)
Declare #RqstUserVal cursor for
Select RequestId
from REQUEST_LIST
where RequestId in (#RsqtIDs)
BEGIN
OPEN RqstUserVal
FETCH NEXT from RqstUserVal into #RqstId
WHILE(##fetch_status <> -1)
BEGIN
SET #UserIDToCheck = (
select UserId from dbo.REQUEST_LIST where RequestId = #RqstId)
Print(#UserIDToCheck)
If(#UserIDToCheck != #UserID)
SET #Result = 99 ;
--Fetch the next row from the cursor
FETCH RqstUserVal into #RqstId
END
END
CLOSE RqstUserVal
Deallocate RqstUserVal
RETURN #Result
END
Thanks in advance
Depending on your SQL-Server Verion you can use a Table-Valued Function like in this short example
Select * from dbo.test1
Where ID in(
Select * from dbo.F_SplitAsIntTable('1,123,234,456'))
Function defined as
CREATE FUNCTION F_SplitAsIntTable
(
#txt varchar(max)
)
RETURNS
#tab TABLE
(
ID int
)
AS
BEGIN
declare #i int
declare #s varchar(20)
Set #i = CHARINDEX(',',#txt)
While #i>1
begin
set #s = LEFT(#txt,#i-1)
insert into #tab (id) values (#s)
Set #txt=RIGHT(#txt,Len(#txt)-#i)
Set #i = CHARINDEX(',',#txt)
end
insert into #tab (id) values (#txt)
RETURN
END
GO