Concatenating multiple rows fields into one column in T-SQL - asp.net

I am writing an SQL query in which that I will need to perform a sub select on a table, which will usually return multiple rows. I need to be able to join together the results of a certain field from all the rows into one field to output. Is this possible, and how?
For example, if the SQL query returns
id | field
1 | test1
2 | test2
3 | test3
I need the outputted field to be "test1 test2 test3".
Thanks

Here's the for xml trick to do that:
SELECT field + ' ' as [text()]
FROM YourTable
FOR XML PATH ('')
This prints:
test1 test2 test3
It's typically used with an outer apply to execute it once for each row.

declare #sample table(id int, field varchar(20))
insert into #sample values(1,'test1')
insert into #sample values(2,'test2')
insert into #sample values(3,'test3')
declare #result varchar(max) set #result = ''
select #result = #result + ' '+field from #sample
select #result
A SQLCLR custom aggregator would be a an alternative (read better) solution

Try this:
SELECT RTRIM(field)
FROM (
SELECT field + ' ' field
FROM <YOUR_TABLE>
FOR XML PATH('')
) a

As an addition to the existing answers. Try including the COALESCE expression with column name your going to use. This avoids having null values in your concatenated string and avoid your list looking like this. Notice the redundant blank space.
field1 field2 field4 field
Further details can be found here.
GO
DECLARE #tableName VARCHAR(MAX)
SELECT #tableName = COALESCE(#tableName + ' ' ,'') + Name
FROM sys.tables
SELECT #tableName
GO

it is possible to do with a cursor.
declare #field nvarchar(max)
declare #concat nvarchar(max)
set #concat = ''
declare #cursor cursor
set #cursor = cursor for select field from table
open #cursor
fetch next from #cursor into #field
while ##fetch_status = 0
begin
set #concat = concat(#concat,#field)
fetch next from #cursor into #field
end
your exercise is to add space between the concatenated strings :-)

Related

how to select column based on its on order in Table

I want to select Column from Table based on Its Order
like
create Table Products
(
ProductId Int,
ProductName varchar(50)
)
lets Say I don't Know the name of the second column.
How I can get it like :
Select Col1,Col2 From Product
For SQL Server:
You can't do this in the SELECT clause. You can't select based on the order number of the column. You have to list the columns' names you need to select explicitly, otherwise, use SELECT * to list all. Me be if you are using a data reader object or any other ado.net methods to get the data from database you can do something like this, but this will be based on the column names list listed in your SQL statement.
However, you can do something like this dynamically, by reading columns' metadata ordinal_position from information_schema.columns as explained in the following answer:
Is it possible to select sql server data using column ordinal position?
But, you can do this in the ORDER BY clause. You can ORDER BY column number:
SELECT *
FROM TableName
ORDER BY 2; -- for col2
But this is not recommended to use in ORDER BY or in the SELECT (if any). Furthermore, columns order is not significant in the relational model.
Update: If you want to select at least 3 columns from any table parameter passed to your stored procedure. Try this as follows:
Your stored procedure supposed to receive a parameter #tableNameParam. The folowing code should return the first three columns from the #tablenameParam passed to the stored procedure:
DECLARE #col1 AS VARCHAR(100);
DECLARE #col2 AS VARCHAR(100);
DECLARE #col3 AS VARCHAR(100);
DECLARE #tableNameParam AS VARCHAR(50) = 'Tablename';
DECLARE #sql AS VARCHAR(MAX) ;
SELECT #col1 = column_name FROM information_schema.columns
WHERE table_name = #tableNameParam
AND ordinal_position = 1;
SELECT #col2 = column_name FROM information_schema.columns
WHERE table_name = #tableNameParam;
AND ordinal_position = 2;
SELECT #col3 = column_name FROM information_schema.columns
WHERE table_name = #tableNameParam;
AND ordinal_position = 3;
SET #sql = 'SELECT ' + col1 + ',' + col2 ' + 'col3 ' + FROM ' + #tablename;
you always can do
select * from Product
I'd like to share the following code as a solution to CRUD processing on Ordinal Position within a table. I had this problem today and it took me quite a long time to research and find a working solution. Many of the posted answers indicated that it was not possible to interact with the tables columns on an Ordinal bases but as indicated in the post above using the information_schema table will allow using the column position.
My situation was interacting with a table populated through the use of a pivot view so the columns are always changing based on the data, which is fine in a view result but when the dataset is stored into a table the columns are dynamic. The column names are a Year-Month combination such as 201801, 201802 with an Item Number as a primary key. This pivot table is to indicate manufacturing quantities by Year-Month on a rolling 12 month period so each month the column names with change/shift which changes their ordinal position when the table is rebuilt each month.
The Pivot view is used to build the Staging table, The Staging table is used to build the
Target table so the ordinal position of the staging and target tables are lined up with the same ordinal position.
Declare #colname Varchar(55) -- Column Name
Declare #ordpos INT -- Ordinal Position
Declare #Item Varchar(99) -- PK
Declare #i INT -- Counter
Declare #cnt INT -- Count
Declare #ids table(idx int identity(1,1), Item Varchar(25))
-- Item List
Insert INTO #ids Select Item From DBName.Schema.TableName
select #i = min(idx) - 1, #cnt = max(idx) from #ids
-- Row Loop
While #i < #cnt
Begin
Select #i = #i + 1
Set #ordpos=3
Set #Item = (select Item from #ids where idx = #i)
-- Column Loop
While #ordpos < 27
Begin
Select #colname =column_name From INFORMATION_SCHEMA.Columns Where table_name='TargetTable' and ordinal_position=#ordpos
Exec ('Update TargetTable set ['+#colname+']= (Select ['+#colname+'] From StagingTable Where Item='''+#Item+''') where Item='''+#Item+'''')
Set #ordpos=#ordpos + 1
End -- End Column Loop
End -- End Row Loop
The code here will loop through the Item matrix by rows and by columns and uses Dynamic SQL to build the action, in this case the action is an update but it could just as easily be a select. Each column is processed through the While Loop and then loops through the next row. This allows updates to a specific cell in the matrix by (Item X YearMonth) without actually knowing what the column name at a given position.
The one concern is that depending on the size of the data in this matrix it can be SLOW. I just wanted to show this as a way to use unknown column names in an ordinal position.

How to generate columns dynamically?

I have table with certain number of columns.
I want to populate other table with the data of a particular column of Table1 as columns of table2 dynamically.
When I say dynamically I mean to say that when ever any data is added to the column of Table1 the table2 is populated with as many number of columns.
Changing the schema on the fly really isn't a good idea, for a number of reasons. From what you've described, I think you would be better off using a view for this. A view will give you the dynamic capabilities you're looking for with fewer side effects.
See this article:
How to create a view in SQL Server
I will once again repeat the disclaimer that this is a bad idea, many things can go wrong, and I'm certain there is a better solution to whatever underlying problem you're trying to solve. That said, to answer the explicit question anyway, here is an example of how to do this:
USE tempdb;
GO
CREATE TABLE dbo.Table1(Description VARCHAR(32));
CREATE TABLE dbo.Table2(ID INT);
GO
CREATE TRIGGER dbo.CatchNewTable1Data
ON dbo.Table1
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += CHAR(13) + CHAR(10) +
'ALTER TABLE dbo.Table2 ADD '
+ QUOTENAME(d) + ' VARCHAR(255);' -- guessing on destination data type
FROM
(
SELECT DISTINCT d = LEFT([Description], 128) -- identifier <= 128
FROM inserted AS i
WHERE NOT EXISTS
(
SELECT 1 FROM sys.columns
WHERE name = LEFT(i.[Description], 128)
AND [object_id] = OBJECT_ID('dbo.Table2')
)
) AS x;
EXEC sp_executesql #sql;
END
GO
Now, let's try it out! Try a column that already exists, a multi-row insert where one of the columns already exists, a multi-row insert with dupes, etc. I am not posting a value > 255 nor am I dealing with any fancy characters that will cause a problem. Why? Because ultimately I don't want you to use this solution, I want to solve the real problem. But for the googlers I want to show that there is a solution to the stated problem.
-- does nothing:
INSERT dbo.Table1 SELECT 'ID';
-- only adds column 'foo':
INSERT dbo.Table1 SELECT 'ID'
UNION ALL SELECT 'foo';
-- adds both of these columns:
INSERT dbo.Table1 SELECT 'bar'
UNION ALL SELECT 'splan foob';
-- only adds one of these:
INSERT dbo.Table1 SELECT 'blat'
UNION ALL SELECT 'blat';
SELECT * FROM dbo.Table2;
Results:
ID foo bar splan foob blat
----------- ------------ ------------ ------------ ------------
Don't forget to clean up:
DROP TABLE dbo.Table1, dbo.Table2;

Ordering SQL Server results by IN clause

I have a stored procedure which uses the IN clause. In my ASP.NET application, I have a multiline textbox that supplies values to the stored procedure. I want to be able to order by the values as they were entered in the textbox. I found out how to do this easily in mySQL (using FIELD function), but not a SQL Server equivalent.
So my query looks like:
Select * from myTable where item in #item
So I would be passing in values from my application like '113113','112112','114114' (in an arbitrary order). I want to order the results by that list.
Would a CASE statement be feasible? I wouldn't know how many items are coming in the textbox data.
How are you parameterising the IN clause?
As you are on SQL Server 2008 I would pass in a Table Valued Parameter with two columns item and sort_order and join on that instead. Then you can just add an ORDER BY sort_order onto the end.
From KM's comment above...
I know you didn't state it is comma seperated, but if it was a CSV or even if you have it space seperated you could do the following.
DECLARE #SomeTest varchar(100) --used to hold your values
SET #SomeTest = (SELECT '68,72,103') --just some test data
SELECT
LoginID --change to your column names
FROM
Login --change to your source table name
INNER JOIN
( SELECT
*
FROM fn_IntegerInList(#SomeTest)
) n
ON
n.InListID = Login.LoginID
ORDER BY
n.SortOrder
And then create fn_IntegerInList():
CREATE FUNCTION [dbo].[fn_IntegerInList] (#InListString ntext)
RETURNS #tblINList TABLE (InListID int, SortOrder int)
AS
BEGIN
declare #length int
declare #startpos int
declare #ctr int
declare #val nvarchar(50)
declare #subs nvarchar(50)
declare #sort int
set #sort=1
set #startpos = 1
set #ctr = 1
select #length = datalength(#InListString)
while (#ctr <= #length)
begin
select #val = substring(#InListString,#ctr,1)
if #val = N','
begin
select #subs = substring(#InListString,#startpos,#ctr-#startpos)
insert into #tblINList values (#subs, #sort)
set #startpos = #ctr+1
end
if #ctr = #length
begin
select #subs = substring(#InListString,#startpos,#ctr-#startpos)
insert into #tblINList values (#subs, #sort)
end
set #ctr = #ctr +1
set #sort = #sort + 1
end
RETURN
END
This way your function creates a table that holds a sort order namely, SortOrder and the ID or number you are passing in. You can of course modify this so that you are looking for space rather then , values. Otherwise Martin has the right idea in his answer. Please note in my example I am using one of my tables, so you will need to change the name Login to whatever you are dealing with.
the same way you concatenate ('113113','112112','114114') to pass to the sql sentence in the where clausule you can concatenate
order by
case item
when '113113' then 1
when '112112' then 2
when '114114' then 3
end
to pass to your order by clausule

SQL use comma-separated values with IN clause

I am developing an ASP.NET application and passing a string value like "1,2,3,4" into a procedure to select those values which are IN (1,2,3,4) but its saying "Conversion failed when converting the varchar value '1,2,3,4' to data type int."
Here is the aspx code:
private void fillRoles()
{
/*Read in User Profile Data from database */
Database db = DatabaseFactory.CreateDatabase();
DbCommand cmd = db.GetStoredProcCommand("sp_getUserRoles");
db.AddInParameter(cmd, "#pGroupIDs", System.Data.DbType.String);
db.SetParameterValue(cmd, "#pGroupIDs", "1,2,3,4");
IDataReader reader = db.ExecuteReader(cmd);
DropDownListRole.DataTextField = "Group";
DropDownListRole.DataValueField = "ID";
while (reader.Read())
{
DropDownListRole.Items.Add((new ListItem(reader[1].ToString(), reader[0].ToString())));
}
reader.Close();
}
Here is my procedure:
CREATE Procedure [dbo].[sp_getUserRoles](#pGroupIDs varchar(50))
AS BEGIN
SELECT * FROM CheckList_Groups Where id in (#pGroupIDs)
END
Here is a workaround I found to do what you are trying to achieve
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
SELECT * FROM CheckList_Groups Where (',' + #pGroupIDs +',' LIKE '%,' + CONVERT(VARCHAR, id) + ',%')
End
This gets your comma delimited list and compares it to the id's(which are represented like so ',1,', ',2,' etc) in the table using LIKE
If you dont want to use dynamic sql, the best way ive found is to create a function which turns a delimited string into a table, something like this works for an Integer list:
CREATE FUNCTION [dbo].[StringToIntList]
(#str VARCHAR (MAX), #delimeter CHAR (1))
RETURNS
#result TABLE (
[ID] INT NULL)
AS
BEGIN
DECLARE #x XML
SET #x = '<t>' + REPLACE(#str, #delimeter, '</t><t>') + '</t>'
INSERT INTO #result
SELECT DISTINCT x.i.value('.', 'int') AS token
FROM #x.nodes('//t') x(i)
ORDER BY 1
RETURN
END
Then use that in your sp:
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
SELECT * FROM CheckList_Groups Where id in (
SELECT ID FROM dbo.StringToIntList(#pGroupIds,',')
)
End
Sure it can't do that,
The generated query would be sth like this
SELECT * FROM CheckList_Groups Where id in ('1,2,3,4')
and sure it can't be executed.
you can build the query in your stored procedure then execute it with exec
'SELECT * FROM CheckList_Groups Where id in (' + #pGroupIDs + ')'
or
SELECT * FROM CheckList_Groups Where charindex(','+id+',' , #pGroupIDs)>0
but you first must add the ',' to start and end of your parameter in your c# code
It is not possible to put those values (the comma separated string) in a parameter-value.
What you'll have to do, is to create the SQL Statement in your stored procedure dynamically, by string concatenation. You'll have to execute it with the sp_executesql stored procedure then.
CREATE PROCEDURE [dbo].[getUserRoles]( #groupIds NVARCHAR(50) )
AS BEGIN
DECLARE #statement NVARCHAR(255)
SELECT #statement = N'SELECT * FROM CheckList_Groups Where id in ( ' + #pGroupIDs + N')'
execute sp_executesql #statement
END
Also, not that I named the SP getUserRoles instead of sp_getUserRoles.
The reason is very simple: when you execute a stored procedure whose name starts with sp_, then SQL Server will first query the master database to find that stored procedure, which causes a performance hit offcourse.
The way you are trying to do this is slightly wrong. You will need to use EXECUTE in order to achieve this.
CREATE PROCEDURE [dbo].[sp_getUserRoles](#pGroupIDs nvarchar(50))
As
BEGIN
EXECUTE (N'SELECT * FROM CheckList_Groups Where id in (' + #pGroupIDs + ')';
END
DECLARE #TagId NVARCHAR(100) = '43,81'
SELECT * FROM TABLE WHERE TagId IN (SELECT TRIM(VALUE) FROM STRING_SPLIT( #TagId , ',' ) )
USE STRING_SPLIT FUNCTION FOR THIS
You need to use SP_executesql to achieve this functionllity
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
EXECUTE sp_executesql
N'SELECT * FROM CheckList_Groups Where id in (#pGroupIDs)',
N'#level varchar(50)',
#level = #pGroupIDs;
End
The IN clause can't take a bound parameter like that. What it's being given when the query is actually created is SELECT * FROM CheckList_Groups Where id in ('1,2,3,4'). Essentially the IN clause is being passed a single string.
First create function -
Just run this code
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[CSVToTable] (#InStr VARCHAR(MAX))
RETURNS #TempTab TABLE
(id int not null)
AS
BEGIN
;-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',')
DECLARE #SP INT
DECLARE #VALUE VARCHAR(1000)
WHILE PATINDEX('%,%', #INSTR ) <> 0
BEGIN
SELECT #SP = PATINDEX('%,%',#INSTR)
SELECT #VALUE = LEFT(#INSTR , #SP - 1)
SELECT #INSTR = STUFF(#INSTR, 1, #SP, '')
INSERT INTO #TempTab(id) VALUES (#VALUE)
END
RETURN
END
GO
Then -
Use function in bracket with select statment -
DECLARE #LIST VARCHAR(200)
SET #LIST = '1,3'
SELECT Id, Descr FROM CSVDemo WHERE Id IN (SELECT * FROM dbo.CSVToTable(#LIST))

Passing comma-separated value from .NET to stored procedure using the value in "IN" SQL function

I have an SQL query similar to the following:
create procedure test
(
#param1 nvarchar(max)
)
as
begin
select * from table where column1 in (#param1)
end
Now I need to pass the value of #param1 from my .net application in such a way that the above query works.
Can some one please advise me on how to pass from my VB.NET code a value which is similiar to below:
'1','2','3'
My main question is how do I structure value of parameter like above example from my .NET application?
quickly like that, I would create a table valued function that would parse it so you can do
select *
from table
where field in (select field from dbo.myfunction(#param1))
For this type of thing, I use this function and use it as follows:
select Column1, column2 from my table where ID in (select item from fnSplit('1,2,3,4,5,6',','))
create FUNCTION [dbo].[fnSplit](
#sInputList VARCHAR(8000) -- List of delimited items
, #sDelimiter VARCHAR(8000) = ',' -- delimiter that separates items
)
RETURNS #List TABLE (item VARCHAR(8000))
BEGIN
DECLARE #sItem VARCHAR(8000)
WHILE CHARINDEX(#sDelimiter,#sInputList,0) <> 0
BEGIN
SELECT
#sItem=RTRIM(LTRIM(SUBSTRING(#sInputList,1,CHARINDEX(#sDelimiter,#sInputList,0)-1))),
#sInputList=RTRIM(LTRIM(SUBSTRING(#sInputList,CHARINDEX(#sDelimiter,#sInputList,0)+LEN(#sDelimiter),LEN(#sInputList))))
IF LEN(#sItem) > 0
INSERT INTO #List SELECT #sItem
END
IF LEN(#sInputList) > 0
INSERT INTO #List SELECT #sInputList -- Put the last item in
RETURN
END
I don't think the problem is in the values you are passing. #param1 is just a string.
You need to address this in your procedure. Your select statement will not be able to recognize the values in you IN clause. One solution is to take the comma-separated string and insert each record into a table variable Explained Here
If your table variable is table #param_list, you procedure test looks like:
create procedure test ( #param1 nvarchar(max) )
as begin
select * from table where column1 in (Select thefield from #param_list);
end

Resources