Loop N table columns and insert concatenated string into same row - tsql

I have a string list with concatenated value separated by delimiter like this:
DECLARE #stringlist NVARCHAR(MAX) = 'company_no,emp_no,emp_name,emp_type,salary_type....'
And I have a temp table with dynamic number of nameless columns with following data:
------------------------------
001 A01 John P A
001 A05 Mary P A
I want to split the string and insert each split string into each column in one row, such as below:
-----------------------------------------------
company_no emp_no emp_name emp_type salary_type
001 A01 John P A
001 A05 Mary P A
I can split the string into multiple rows. But how do I split and loop each column in temp table to insert these value?

Assuming by your sample data and desired result that all columns in the destination table are some string type, I would suggest not to split the string at all.
Instead, create a dynamic sql insert statement from it:
DECLARE #Sql nvarchar(max);
SET #Sql = 'INSERT INTO <TempTableNameHere>
VALUES ('''+ REPLACE(#stringList, ',', ''',''') + ''');';
EXEC(#Sql);

Related

Use table column value as calculation for result in another column

I have a table with a column named "Calc" varchar(50). This column contains math calculations such as 1+1, 9*9, 10/2, 10-2 etc.
Is there way in an update query to apply this calculation from the column and output the result in the same table in the "Results" column varchar(50).
As you know by now, SQL Server does not have an EVAL() function, nor does it support macro substitution.
However, this can be done via dynamic SQL
Example
Create table #YourTable (id int,[Calc] varchar(150),Results varchar(150))
Insert Into #YourTable Values
(1,'1+1',null)
,(2,'9*9',null)
,(3,'10/2',null)
,(4,'10-2',null)
,(5,'datediff(DAY,''2018-01-01'',getdate())',null) -- Added Just for Fun
Declare #SQL varchar(max) = Stuff((Select ',' + concat('(',ID,',',[Calc],')')
From #YourTable A
For XML Path (''))
,1,1,'')
Exec('Update A set Results = B.Value
From #YourTable A
Join ( Select * from (values ' + #SQL + ')A([ID],[Value]) ) B
on A.ID = B.ID
')
Select *
From #YourTable
The Updated Table
id Calc Results
1 1+1 2
2 9*9 81
3 10/2 5
4 10-2 8
5 datediff(DAY,'2018-01-01',getdate()) 1012

Check if a number is in an array, which is the content of a SQL database field

A SQL database field has an array as the content (comma separated values, all integer numbers). I need to check if a number is in this array and, if yes, then the record is filtered on a select query.
A simple solution would be: suppose a function like 'Is_In' below:
select * from table where #number Is_In([fieldWithArrayContent])
I expect there is a SQL in function or even a function that can be written and used to solve this issue.
You need a splitter function - for best performane I suggest DelimitedSplit8k. Then you could just do this:
-- Sample Data
DECLARE #sometable TABLE (someid INT IDENTITY, someArray VARCHAR(1000));
INSERT #sometable(someArray)
VALUES('1,2,10,12'),('5,6,7'),('10,12,10,20,10,10'),('1,2,3'); -- id 1 & 3 have the value "10"
-- Variable
DECLARE #number INT = 10;
SELECT DISTINCT t.someid, t.someArray
FROM #sometable AS t
CROSS APPLY dbo.delimitedSplit8k(t.someArray,',') AS s
WHERE #number = s.item;
Returns:
someid someArray
----------- ------------------------
1 1,2,10,12
3 10,12,10,20,10,10
Using the same example as Alan, we can do that without a splitting function with some string manipulation as below:-
DECLARE #sometable TABLE (someid INT IDENTITY, someArray VARCHAR(1000));
INSERT #sometable(someArray)
VALUES('1,2,10,12'),('5,6,7'),('10,12,10,20,10,10'),('1,2,3'); -- id 1 & 3 have the value "10"
-- Variable
DECLARE #number INT = 1 --or 10 will work
Declare #seperator varchar(1)=','
Declare #search varchar(50)=CONCAT('%',#seperator,cast(#number as varchar(10)),#seperator,'%')
SELECT t.someid, t.someArray
FROM #sometable AS t
WHERE CONCAT(#seperator,someArray,#seperator) like #search

Transpose columns and rows in Firebird 2.5

I've written a procedure in Firebird (Dialect 3), which returns me something like this:
column1 | column2 | column3 | column4 | ...
----------|-------------|-----------|------------|--------
1 | 55 | 2.5 | 100€ | ...
The specific column names don't really matter. I access it like this
SELECT * FROM MY_PROCEDURE(:START_DATE, :END_DATE);
It only return one row so I guess I could also access it with EXECUTE_PROCEDURE.
Now what I want is to transpose the columns and the rows in the return
row | result
----------|---------
column1 | 1
column2 | 55
column3 | 2.0
column4 | 100€
... | ...
What I initially did is somethink like this:
select 'column1' AS row, column1 AS result
FROM MY_PROCEDURE(:START_DATE, :END_DATE)
union all
select 'column2' AS row, column2 AS result
FROM MY_PROCEDURE(:START_DATE, :END_DATE)
union all
...
Basically one query for each column. It worked. However, eventually I ran into this problem:
Dynamic SQL Error
Too many Contexts of Relation/Procedure/Views. Maxium allowed is 255.
So I need to restructure my script. As you can see, my SQL knowledge is pretty mediocre, and I simply don't know how to fetch each column as a row in a single select.
Would anyone be able to help? Thanks in advance.
Firebird by itself as no unpivot or other built-in support for transposing columns.
The 'best' solution, and probably the most performing solution would be to rewrite MY_PROCEDURE (or write an alternative version) to output the rows transposed.
For example, assuming your stored procedure does something like this:
set term #;
create procedure test_1
returns (id integer, column1 double precision, column2 double precision, column3 double precision)
as
begin
for
select id, column1, column2, column3
from sometable
into :id, :column1, :column2, :column3 do
begin
suspend;
end
end#
set term ;#
You can then rewrite this by manually transposing the values into separate suspends:
set term #;
create procedure test_2
returns (id integer, columnname varchar(100), columnvalue double precision)
as
declare column1 double precision;
declare column2 double precision;
declare column3 double precision;
begin
for
select id, column1, column2, column3
from sometable
into :id, :column1, :column2, :column3 do
begin
columnname = 'column1';
columnvalue = column1;
suspend;
columnname = 'column2';
columnvalue = column2;
suspend;
columnname = 'column3';
columnvalue = column3;
suspend;
end
end#
set term ;#
This will output something like
id columnname columnvalue
1 column1 1.0
1 column2 1.5
1 column3 5.0
2 ...etc
This solution does require that all output (columnvalue) has the same type. Otherwise you will need to cast to a common data type.
Alternatively, you could chain the first procedure into the second procedure by using for select * from test_1 into .... This maybe more or less efficient depending on the internals of your stored procedure:
set term #;
create procedure test_3
returns (id integer, columnname varchar(100), columnvalue double precision)
as
declare column1 double precision;
declare column2 double precision;
declare column3 double precision;
begin
for
select id, column1, column2, column3 from test_1
into :id, :column1, :column2, :column3 do
begin
columnname = 'column1';
columnvalue = column1;
suspend;
columnname = 'column2';
columnvalue = column2;
suspend;
columnname = 'column3';
columnvalue = column3;
suspend;
end
end#
set term ;#
This last option is probably best if you need both variants of the output, as this means you will only have single place for the logic of that stored procedure.
For ad-hoc querying, you can also replace the stored procedure with an execute block with the same code.

crystal report replace function

I have two tables:
Table 1:
Id stringval
-- ---------
1 do you work on date XXXX and date ####
Table 2:
Id CharString ValueString
-- ---------- -----------
1 XXXX 5-5-2013
2 #### 10-5-2013
I want to return the following string value:
do you work on date 5-5-2013 and date 10-5-2013
Can this be achieved using Crystal Reports?
Or, can it be achieved using Sql Server?
Assuming that XXXX, ####, or any other values in the CharString column of Table2 would only appear as placeholders it is also possible to achieve your goal using sql server. A query like this would do it:
DECLARE #CharString VARCHAR(100)
DECLARE #ValueString VARCHAR(100)
DECLARE #SearchString VARCHAR(100)
DECLARE #ResultString VARCHAR(100)
SELECT #SearchString = stringval FROM Table1
SELECT #ResultString = stringval FROM Table1
DECLARE c CURSOR FAST_FORWARD FOR
SELECT CharString, ValueString
FROM Table2
OPEN c
FETCH NEXT FROM c INTO #CharString, #ValueString
WHILE ##FETCH_STATUS = 0
BEGIN
IF PATINDEX('%' + #CharString + '%', #SearchString) <> 0
SET #ResultString = REPLACE(#ResultString, #CharString, #ValueString)
FETCH NEXT FROM c INTO #CharString, #ValueString
END
CLOSE c
DEALLOCATE c
SELECT #ResultString
The option here would be take the string value from table and manuplate the string the using string functions of crystal reports.
I am not sure how this will work but give a try.
take do you work on date XXXX and date #### into a string and split by space then extact XXXX and #### now take the required values 5-5-2013 and 10-5-2013 in to varaibles now join the strings like
"do you work on data "+5-5-2013+" and date "+10-5-2013

T-SQL Loop in a stored proc

how do I loop through a comma separated variable using tsql in a stored proc
So for instance my list would look like this
"1,2,3,4,5,6,7,8,9,10"
and I would loop thought this list and made some necessary table
insert based on this list
You could do it a couple ways, but if this would be a list of ID's it could be done like this as well. It would change your list format a bit.
UPDATE table
SET column = value
WHERE ID in ('1','2','3','4','5','6','7','8','9','10')
You could do a loop as well
DECLARE #List CHAR(100)
DECLARE #ListItem int
DECLARE #Pos int
SET #List = '1,2,3,4,5,6,7,8,9,10'
WHILE LEN(#List) > 0
BEGIN
--Pull Item Frim List
SET #Pos = CHARINDEX(',', #List)
IF #Pos = 0
BEGIN
SET #ListItem = #List
END
ELSE
BEGIN
SET #ListItem = SUBSTRING(#List, 1, #Pos - 1)
END
UPDATE table
SET column = value
WHERE ID = #ListItem
--Remove Item Frim List
IF #Pos = 0
BEGIN
SET #List = ''
END
ELSE
BEGIN
SET #List = SUBSTRING(#List, #Pos + 1, LEN(#List) - #Pos)
END
END
I'd try to avoid looping and insert the rows directly from your comma list.
Use a table values parameter (new in SQl Server 2008). Set it up by creating the actual table parameter type:
CREATE TYPE IntTableType AS TABLE (ID INTEGER PRIMARY KEY)
Your procedure would then be:
Create Procedure up_TEST
#Ids IntTableType READONLY
AS
SELECT *
FROM ATable a
WHERE a.Id IN (SELECT ID FROM #Ids)
RETURN 0
GO
if you can't use table value parameters, see: "Arrays and Lists in SQL Server 2005 and Beyond, When Table Value Parameters Do Not Cut it" by Erland Sommarskog, then there are many ways to split string in SQL Server. This article covers the PROs and CONs of just about every method. in general, you need to create a split function. This is how a split function can be used to insert rows:
INSERT INTO YourTableA (colA)
SELECT
b.col1
FROM dbo.yourSplitFunction(#Parameter) b
I prefer the number table approach to split a string in TSQL but there are numerous ways to split strings in SQL Server, see the previous link, which explains the PROs and CONs of each.
For the Numbers Table method to work, you need to do this one time table setup, which will create a table Numbers that contains rows from 1 to 10,000:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Once the Numbers table is set up, create this split function:
CREATE FUNCTION [dbo].[FN_ListToTable]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN
(
----------------
--SINGLE QUERY-- --this will not return empty rows
----------------
SELECT
ListValue
FROM (SELECT
LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(#SplitOn, List2, number+1)-number - 1))) AS ListValue
FROM (
SELECT #SplitOn + #List + #SplitOn AS List2
) AS dt
INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
WHERE SUBSTRING(List2, number, 1) = #SplitOn
) dt2
WHERE ListValue IS NOT NULL AND ListValue!=''
);
GO
You can now easily split a CSV string into a table and join on it:
Create Procedure up_TEST
#Ids VARCHAR(MAX)
AS
SELECT * FROM ATable a
WHERE a.Id IN (SELECT ListValue FROM dbo.FN_ListToTable(',',#Ids))
GO
or insert rows from it:
Create Procedure up_TEST
#Ids VARCHAR(MAX)
,#OtherValue varchar(5)
AS
INSERT INTO YourTableA
(colA, colB, colC)
SELECT
ListValue, #OtherValue, GETDATE()
FROM dbo.FN_ListToTable(',',#Ids)
GO
Using CTE (Common Table Expression) is the most elegant solution I think check this question on stackoverflow,
T-SQL: Opposite to string concatenation - how to split string into multiple records