I want to know, if exist a way to create a CURSOR with a variable column names
Example:
I have this table:
Translation image for text
ID | TEST1 | TEST2 | TEST3
7 1 3 1
8 2 3 4
9 3 4 5
10 3 3 1
11 2 3 4
12 3 4 5
13 1 3 1
14 2 3 4
15 3 4 5
SQL Code:
DECLARE
#count int,
#columnX varchar(5),
#aux2previous int,
#aux2 int,
#aux1 int,
#columnXResult int,
#id int;
SET #aux2previous = 0;
SET #count = 1;
SET #columnX = 'test' + count;
DECLARE cursor1 CURSOR FOR
SELECT ID, #columnX FROM table
OPEN cursor1
FETCH NEXT FROM cursor1 INTO #id,#columnXResult
...
SET #aux1 = #columnXResult+ #aux2previous
SET #aux2 = #aux2previous + 1
SET #string = 'SXW_'+#columnX+'_'+#aux1+'<>'+#aux2
INSERT INTO tblAuxiliary VALUES(#aux1,#aux2,#string)
SET #count = #count + 1;
SET #aux2previous = #aux2
...
Translation image for text
Foreach row in my first table i have a new row here:
for column Test1
AUX1 | AUX2 | STRING
1 1 SXW_Test1_1<>1
3 2 SXW_Test1_3<>2
for column Test2
AUX1 | AUX2 | STRING
3 1 SXW_Test2_3<>1
6 4 SXW_Test2_6<>4
for column Test3
AUX1 | AUX2 | STRING
1 1 SXW_Test3_1<>1
5 2 SXW_Test3_5<>2
when i do with #columnX
SELECT ID, #columnX FROM table
i got this error:
Conversion failed when converting the varchar value 'test1' to data type int
I see something like #sql = 'select '+#columnX ..., but i need to exec it and i can't do this with cursor.
It is example how use dynamic sql and cursor:
create table test (
t1 int not null,
t2 varchar(10) not null
)
declare #query varchar(1000)
select #query = '
declare #column int
declare query cursor for
select t'+cast(1 as varchar(1))+' from test
open query
fetch next from query into #column
while(##FETCH_STATUS=0)
begin
select #column
fetch next from query into #column
end
close query
deallocate query
'
exec (#query)
There's no way to create a cursor with variable column names unless you use dynamic SQL: you build up a string of SQL code and use EXEC sp_executesql to run it. It's messy and difficult to get right, but you can do it with some trial and error (preferably on a development system, not in production).
FWIW, I think some of the comments above a being a little harsh because they've forgotten what it's like to start out in their field: you're doing fine. Cursors are often misused by web developers who think procedurally instead of "set based", but I still use them occasionally where they make sense (or are just easier). If you want, you can post a new question with more details about what you're trying to accomplish and ask "how can I get rid of this cursor". But if the performance with the cursor is okay, don't worry about it.
HTH!
Check this out.
DECLARE #table TABLE(ID INT,test1 INT,test2 INT,test3 INT)
INSERT INTO #TABLE VALUES
(7,1,3,1),(8,2,3,4),(9,3,4,5),(10,3,3,1),
(11,2,3,4),(12,3,4,5),(13,1,3,1),(14,2,3,4),
(15,3,4,5)
SELECT top 2
t.test1 AUX1,(t.test1+T.prev_test1) AUX2,concat('SXW_Test1_',t.test1,'<>',(t.test1+T.prev_test1)) as string FROM
(
SELECT test1,
LAG(test1,1,0) OVER (ORDER BY ID) prev_test1
FROM #table
) t
union all
SELECT top 2
t.test2 AUX1,(t.test2+T.prev_test2) AUX2,concat('SXW_Test2_',t.test2,'<>',(t.test2+T.prev_test2)) as string FROM
(
SELECT test2,
LAG(test2,1,0) OVER (ORDER BY ID) prev_test2
FROM #table
) t
union all
SELECT top 2
t.test3 AUX1,(t.test3+T.prev_test3) AUX2,concat('SXW_test3_',t.test3,'<>',(t.test3+T.prev_test3)) as string FROM
(
SELECT test3,
LAG(test3,1,0) OVER (ORDER BY ID) prev_test3
FROM #table
) t
Related
I have this table:
-----------------------------------------------
code intvalue checkrole result
-----------------------------------------------
A01 14 A02-A03 true
A02 24 A04 false
A03 10 A04 false
A04 12 A02/2 true
I would like to fill the column result with a query or sp, based on the role described into the column checkrole, something like Excel, any ideas?
I apologize to everyone, I explained myself wrongly.
For example:
A01 14 A02-A03 true
in this case, I would like to interpret the role and get 24-10 = 14 ie true
UPDATE 2:
Hello everyone and thanks for your interest. HABO, I'm working in this direction "substitute the values into the expression (" 24-10 ") ....":
TBL
code intValue checkRule result
A01 14 select A02-A03 from Table_1 NULL
A02 24 select A04 from Table_1 NULL
A03 10 select A04 from Table_1 NULL
A04 12 select A02 / 2 from Table_1 NULL
SP
CREATE PROCEDURE [dbo]. [Test]
AS
BEGIN
DECLARE #code VARCHAR (50)
DECLARE #intvalue INT
DECLARE #checkrule VARCHAR (50)
DECLARE #cTbl AS CURSOR
SET #cTbl = CURSOR FOR SELECT code
, intValue
, checkRule
FROM [dbo]. [Table_1]
OPEN #cTbl FETCH NEXT FROM #cTbl INTO #code, #intvalue, #checkrule
WHILE ## FETCH_STATUS = 0
BEGIN
declare #statement nvarchar (4000), #Result int, #Parm nvarchar (20)
SET #statement = 'select #Result = 11 + 7'
SET #Parm = '#Result int output'
EXEC sp_executesql #statement, #Parm, # Result OUT
print #Result
FETCH NEXT FROM #cTbl INTO #code, #intvalue, #checkrule
END
CLOSE #cTbl
DEALLOCATE #cTbl
END
UPDATE 3:
It was what I was looking for. I'm not an expert, but I learn from mistakes and I thank those who teach me something new, thanks to all those who participated, and above all thanks to HOBO, good evening
I would probably write a function that would create a list of values from your checkRole (eg it would give me list 'A02', 'A03', 'A04' from A02-A04 and then I would write an update statement with 'contains' statement in it
using the table that you show you can update the values of result column with the next query
UPDATE TableName
SET result = CASE
WHEN checkrole = 'A02-A03' THEN "true"
WHEN checkrole = 'A04' THEN "false"
WHEN checkrole = 'A02/2' THEN "true"
ELSE "setDefaultValue"
END
WHERE code in (A01,A02,A03,A04);
at the last line you specify the code value of row to update. If you execute this query with the column result empty, it will complete the table and result into the same data as the one you post above.
(The query work good on Mysql)
Executive Summary: This is a terrible idea. Back away slowly and no one gets hurt.
Disclaimer: I'm not responsible for the costs of any therapy required after reading beyond this point.
From your Update 2 it appears that you have control over the format of CheckRule. It is much easier to require that the user delimit the Codes, e.g. using '«A04»' rather than 'A04', instead of trying to find them using TSQL. You don't need to worry about problems like substituting 'A02' with 66 when the CheckRule is 'A026 - BA02' and getting '666 - B66'.
What follows is simply dreadful. It demonstrates a brute force approach to taking expressions with delimited "variables", e.g. '«A04»', replacing all occurrences of each "variable" with its integer value, evaluating the resulting expression and retrieving the result.
Instead of using something as distasteful as a cursor, it uses two nested cursors to waddle through the CheckRules and Codes. It would be more efficient to parse the "variables" out of the expression and substitute values as needed rather than using a try-everything approach, but TSQL doesn't excel at implementing parsers.
For extra credit it also brings along the blessing of embracing SQL Injection. Try a CheckRule like 'A01; select 1 / 0;'. Don't try one like '-1; drop database LuckyGuess;'.
-- Sample data.
declare #Samples as Table
( SampleId Int Identity, Code NVarChar(8), IntValue Int, CheckRule NVarChar(64), Result Bit );
insert into #Samples ( Code, IntValue, CheckRule ) values
( N'A01', 14, N'«A02» - «A03»' ),
( N'A02', 24, N'«A04»' ),
( N'A03', 10, N'«A04»' ),
( N'A04', 12, N'«A02» / 2' );
select * from #Samples;
-- Process the data.
declare Oy cursor forward_only fast_forward read_only
for select SampleId, Code, IntValue, CheckRule from #Samples;
declare #SampleId as Int, #Code as NVarChar(8), #IntValue as Int, #CheckRule as NVarChar(64);
declare Vey cursor forward_only fast_forward read_only
for select Code, IntValue from #Samples;
declare #VariableCode as NVarChar(8), #VariableIntValue as Int
-- For each row's CheckRule ...
open Oy;
fetch next from Oy into #SampleId, #Code, #IntValue, #CheckRule;
while ##Fetch_Status = 0
begin
-- Copy the CheckRule so that we can build the #Expression to evaluate.
declare #Expression as NVarChar(64) = #CheckRule;
-- For each Code that could appear in an #Expression ...
open Vey;
fetch next from Vey into #VariableCode, #VariableIntValue;
while ##Fetch_Status = 0
begin
-- Replace any occurrences of the Code in the #Expression with the corresponding integer value.
set #Expression = Replace( #Expression, N'«' + #VariableCode + N'»',
Cast( #VariableIntValue as NVarChar(10) ) );
fetch next from Vey into #VariableCode, #VariableIntValue;
end;
close Vey;
-- Make the #Expression an executable statement.
set #Expression = N'set #IntResult = ' + #Expression;
declare #Result as Int;
-- Evaluate the #Expression and get the #Result .
execute sp_executesql #Expression, N'#IntResult Int output', #IntResult = #Result output;
select #SampleId as SampleId, #Code as Code, #IntValue as IntValue, #CheckRule as CheckRule,
#Expression as Expression, #Result as Result;
fetch next from Oy into #SampleId, #Code, #IntValue, #CheckRule;
end;
close Oy;
deallocate Oy;
In the event it isn't clear, don't. Just don't.
I have 2 tables in SQL Server:
Table1
ID SkillSetRequired SkillDesc
1 100-1,101-1,102-2,103-3 null
2 100-4,105-2 null
3 100-1,102-2,104-2 null
4 100-5,102-2 null
5 105-1 null
Table2
ID SkillName
100 .Net
101 Java
102 JQuery
103 Sql
104 PHP
105 C
I need to update the SkillDesc column of Table1 with the SkillNames. For example in first row I need to update skilldesc as '.Net,Java,Jquery,SQL'
How can I do this with out using cursors?
I am getting the result in a string from the below query for a single record. But I need to update all the rows.
declare #result varchar(max)
SET #result = ''
SELECT #result = #result + ltrim(rtrim(SkillName)) + ',' from #Table2 where id in(
select SUBSTRING(items, 0 + 1, CHARINDEX('-', items, 1) - 0 - 1) from split('100-1,101-1,102-2,103-3',','))
select #result
Firstly, How did u insert 100-1,101-1,102-2,103-3 into the table ?
Do the below maping while adding the above.
Use a Case statement like
Case
when #Skillset = '100' THEN #desc = .Net
when #Skillset = '101' THEN #desc = Java
....
END
same way for other skill sets.
Then while inserting into the table for every id add the skillset and use the #Desc for the which updates
How did u manage adding 100-1,101-1,102-2,103-3 . This is not the proper way to add...
I think cursor is the best option even though it is taking time to execute.
SELECT ID,SkillSetRequired
FROM #Table1
OPEN #CurSkillUpdate
FETCH NEXT
FROM #CurSkillUpdate INTO #id,#skills
WHILE ##FETCH_STATUS = 0
BEGIN
SET #result = ''
SELECT #result = #result + ltrim(rtrim(SkillName)) + ',' from #Table2 where id in(
select SUBSTRING(items, 0 + 1, CHARINDEX('-', items, 1) - 0 - 1) from split(#skills,','))
update #Table1 set SkillDesc=#result where ID= #id
FETCH NEXT
FROM #CurSkillUpdate INTO #id,#skills
END
I have multiple values in one cell in a table which are separated by an space from each other. this is somehow how my table looks like, there is a space in between each string in every cell:
column1 | column2
|
abc fgt | rty lkj
I want to create another table based on the first table in which "abc" and "rty" are in one row because they both are located in the first place, "fgt" and "lkj" are in another row for the same relational reason (they are in the 2nd place and so on):
column1 | column2
|
abc | rty
fgt | lkj
How can I do that?
You can use a function like this
IF EXISTS(SELECT * FROM sysobjects WHERE ID = OBJECT_ID('UF_CSVToTable'))
DROP FUNCTION UF_CSVToTable
GO
CREATE FUNCTION UF_CSVToTable
(
#psCSString VARCHAR(8000)
)
RETURNS #otTemp TABLE(sID VARCHAR(20),tID VARCHAR(20))
AS
BEGIN
DECLARE #sTemp VARCHAR(10)
DECLARE #tTemp VARCHAR(10)
WHILE LEN(#psCSString) > 0
BEGIN
SET #sTemp = LEFT(#psCSString, ISNULL(NULLIF(CHARINDEX(' ', #psCSString) - 1, -1),
LEN(#psCSString)))
SET #psCSString = SUBSTRING(#psCSString,ISNULL(NULLIF(CHARINDEX(' ', #psCSString), 0),
LEN(#psCSString)) + 1, LEN(#psCSString))
INSERT INTO #otTemp(sID) VALUES (#sTemp)
SET #tTemp = LEFT(#psCSString, ISNULL(NULLIF(CHARINDEX(' ', #psCSString) - 1, -1),
LEN(#psCSString)))
SET #psCSString = SUBSTRING(#psCSString,ISNULL(NULLIF(CHARINDEX(' ', #psCSString), 0),
LEN(#psCSString)) + 1, LEN(#psCSString))
UPDATE #otTemp SET tID=#tTemp WHERE sID=#sTemp
END
RETURN
END
Go
It can be called like this.
select * from UF_CSVToTable('1 2 3 4 5 6 7 15 55 59 86')
You need to pass your column as input parameter.
SQL FIDDLE DEMO
First of all, you can create UDF for splitting values by specific delimiter (in your case this is SPACE):
CREATE FUNCTION [dbo].[Split](#String nvarchar(4000), #Delimiter char(1))
returns #temptable TABLE (rownumber INT , items nvarchar(4000))
as
begin
declare #idx int
declare #slice nvarchar(4000)
DECLARE #rownumber int = 1
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(rownumber, Items) values(#rownumber, #slice)
set #String = right(#String,len(#String) - #idx)
SET #rownumber = #rownumber + 1
if len(#String) = 0 break
end
return
end
GO
After that, it is enough to write simple INSERT INTO SELECT... query:
INSERT INTO dbo.Table_2
( column1, column2 )
SELECT
item1.items ,
item2.items
FROM dbo.Split((SELECT column1 FROM dbo.Table_1), SPACE(1)) AS item1
FULL JOIN dbo.Split((SELECT column2 FROM dbo.Table_1), SPACE(1)) AS item2
ON item1.rownumber = item2.rownumber
Table_1 is table where actually values are, Table_2 is table for final rows.
SQL Fiddle Demo
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have a problem with create query
=========== ============================ =================
ValueTable1 ValueTable2 FilterTable1
=========== ============================ =================
Id |Name Id | RefNo || Property| Value Id|Property|Operator|Value
1 |X 1 1 P1 500 1 P1 > 100
2 1 P2 300 1 P2 = 300
3 1 P3 200 1 P3 <> 100
2 |Y 4 2 P1 250
5 2 P2 100
6 2 P3 200
I have 2 table ValueTable and 1 filter table in my database
I wanna create a query that can filter Valuetabl2 from properties and operators in FilterTable1
I couldnt create it.Is there any way?
Maybe I did misunderstood your question...
You want do apply those Filters on FilterTable1 on the values of table ValueTable2, right?
doing AND condition... with the filters..
to identify what RefNo fill all conditions.. I am right? or did misunderstood at this point?
and this RefNo references the table ValueTable1, right? or I did misunderstood??
if I did undestood right
can try something like this...
create table ##ValueTable1 (id int , name varchar)
insert into ##ValueTable1
values (1,'X'),(2,'Y')
create table ##ValueTable2 (id int , RefNo int, Property nvarchar(max),value int)
insert into ##ValueTable2
values
( 1 , 1 ,'P1', 500),
( 2 , 1 ,'P2', 300),
( 3 , 1 ,'P3', 200),
( 4 , 2 ,'P1', 250),
( 5 , 2 ,'P2', 100),
( 6 , 2 ,'P3', 200)
create table ##FilterTable1 (id int, Property nvarchar(max),Operator nvarchar(max) , value int)
insert into ##FilterTable1
values
(1,'P1','>', 100),
(1,'P2','=', 300),
(1,'P3','<>',100)
declare #sql nvarchar(max)
set #sql = ''
select #sql = #sql + ' intersect select RefNo from ##ValueTable2 where Property = ''' +Property+''' and value '+Operator+CAST(value as varchar(max))
from ##FilterTable1
where id = 1
set #sql = 'select * from ##ValueTable1 T inner join ('+STUFF(#sql,1,10,'') +')V on V.RefNo = T.id'
--just stuff #sql if ##ValueTable1 doesnt matter
--set #sql = STUFF(#sql,1,10,'')
exec(#sql)
drop table ##ValueTable2
drop table ##FilterTable1
drop table ##ValueTable1
my resultset achieved is...
id name RefNo
1 X 1
You have to create a string for your sql statement in a procedure loop for example and then execute it.
See here how to execute it: http://msdn.microsoft.com/de-de/library/ms188332.aspx.
You can not create dynamic sql queries directly.
So create a procedure, create a local cursor to loop to your ValueTable2 and for each property check the operator in FilterTable1, then create a new sql string which you execute then.
EDIT
I don't fully understand your requirement, but this example should give you an approach:
DECLARE #operator varchar(2)
DECLARE #value varchar(255)
DECLARE #sql varchar(max)
DECLARE cur1 CURSOR FOR
SELECT [operator], CAST([value] AS varchar(255))
FROM ValueTable2 v
JOIN FilterTable1 f ON v.Property = f.Property
OPEN cur1
FETCH NEXT FROM cur1
INTO #operator, #value
WHILE ##FETCH_STATUS = 0
BEGIN
#sql = 'SELECT * FROM ValueTable2 WHERE [value] ' + #operator + ' ' + #value
PRINT #sql
-- EXEC sp_executesql #sql
FETCH NEXT FROM cur1 INTO #operator, #value
END
DEALLOCATE cur1
Well, i would try NOT to use CURSORS and/or Dynamic SQL. Cursors are possible of creating serious locking issues, and dynamic SQL is always being (re)compiled, so doing that together is a no go in my point of view.
However, using a temp table with a while loop might be better in stead of cursors. For logic i would say it must be possible to use some if statements..
I made this SQLFiddle (http://sqlfiddle.com/#!3/8a4e3/) to show you what i mean
I have a data set that looks like the below (the input).
IR# CR#
1 1,2
2 3
3 4,5,6
I would like the following output. You can consider all fields varchar for this example.
IR# CR#
1 1
1 2
2 3
3 4
3 5
3 6
I have UDFs to split a CSV string into rows...but not something to split 1 row in a table into multiple rows and then union will the next row, etc.
Thanks!
Use CROSS APPLY in conjunction with your splitting UDF. The string splitter I'm using for my example comes from here.
/* Create function for purposes of demo */
CREATE FUNCTION [dbo].[fnParseStringTSQL] (#string NVARCHAR(MAX),#separator NCHAR(1))
RETURNS #parsedString TABLE (string NVARCHAR(MAX))
AS
BEGIN
DECLARE #position int
SET #position = 1
SET #string = #string + #separator
WHILE charindex(#separator,#string,#position) <> 0
BEGIN
INSERT into #parsedString
SELECT substring(#string, #position, charindex(#separator,#string,#position) - #position)
SET #position = charindex(#separator,#string,#position) + 1
END
RETURN
END
go
/* Set up sample data */
declare #t table (
IR int,
CR varchar(100)
)
insert into #t
(IR, CR)
select 1, '1,2' union all
select 2, '3' union all
select 3, '4,5,6'
/* Here's the query that solves the problem */
select t.IR, p.string
from #t t
cross apply [dbo].[fnParseStringTSQL](t.CR,',') p
/* clean up after demo */
drop function [dbo].[fnParseStringTSQL]