How to get comma-separated values in SQL Server? - sql-server

I have two tables called tblEmployee and tblWorkPlace. The second table consists of multiple rows for each employee. I want to get the result as follows
EmployeeName WorkPlace
Gopi India,Pakistan,...
I.e. if tblWorkPlace consists of multiple rows for an employee, I want the result in a single row, with data being separated by commas.
How will I get this result ????

You'll need to have code on the client side for this. It is possible to make this happen in sql server, but it's not trivial, the performance is horrible, and this kind of thing does not belong on the database anyway.

You're not very clear on how the tables tblWorkplace and tblEmployee are connected - I'm assuming by means of a many-to-many link table or something.
If that's the case, you can use something like this:
SELECT
e.EmpName,
(SELECT STUFF(
(SELECT ',' + w.WPName
FROM #Workplace w
INNER JOIN #WorkplaceEmployeesLink we ON w.WorkplaceID = we.WorkplaceID
WHERE we.EmpID = e.EmpID
FOR XML PATH('')
), 1, 1, '')
) AS Workplaces
FROM #Employee e
(I've replaced your tables with my own table variables #Employee and #Workplace etc. for testing)
This gives me an output something like:
EmpName Workplaces
Gopi India,Pakistan
for that one row.
Basically, the internal FOR XML PATH('') selects the list of workplaces for each employee, and prepends each workplace with a ',' so you get something like ,India,Pakistan.
The STUFF method then stuff an empty string into that resulting string, at position one, for a length of 1 - essentially wiping out the first comma, thus you get the list of workplaces as you desired.

You can assign multiple values to a variable in a select statement. I call this "flattening" the records.
declare #WorkPlace varchar(max)
select #WorkPlace = '' -- it is important to be NOT null
select #WorkPlace = #WorkPlace + WorkPlace + ', '
from YourTable
where EmployeeName = 'Gopi'
You may want to add some code to remove the final ',' from the string.

Here's a link to different ways to get comma separated values.

Related

SQL Server : concatenate results of 2 queries as a string

I am new to SQL, and I need to make a string out of the UNION result. I have seen many similar questions but they were either related to concatenating results of a single SQL query, or they were using JOIN on some kind of row id, while I do not need this and do not have any column on which I can use JOIN.
I have the following UNION:
(
SELECT COUNT(*)
FROM [db].[table1]
WHERE [ItemType] = 2
)
UNION
(
SELECT TOP(1) Items
FROM [db].[table2]
WHERE [ItemType] = 2
)
It returns a simple result with two rows:
15
10
15 is the total number of items, and 10 is the number of items left available.
I want to return a table with only one entry 10/15. What is the simplest way to achieve it? Thanks.
A bit of a guess, but perhaps:
SELECT CONCAT((SELECT COUNT(*) FROM [db].[table1] WHERE [ItemType] = 2)),'/',
(SELECT TOP(1) Items FROM [db].[table2] WHERE [ItemType] = 2 ORDER BY {Your Column}));
Note the {Your Column} which you need to replace with an appropriate column to give the correct consistent result. Having a TOP without an ORDER BY can (and will) produce inconsistent results, as tables in SQL Server are stored in unordered heaps; therefore the "TOP 1" will be whatever row SQL Server "finds" (retrieves) first from your table and is effectively random.
You have lurking problems in what you are trying to do.
First, UNION is a distincting operation. if you happen to have 15 total items AND 15 available, you'll only get one row back. That's not what you want. (UNION ALL would fix this, but you don't need UNION stuff at all).
Your next issue(?) may be your data model choice. The second table (Items) has values in it - you are pulling one row out of a particular type, but you don't have any control over which one gets pulled out. It could be any value in the set. If you want the count of items available, as opposed to the first item randomly that SQL picks for you, then you may want to adjust your query. (For "give me the first item and it actually is the count", you would add an ORDER BY into that subselect to help SQL pick the proper "first" order in a given sort. For "I want the count of distinct items in this table, you might want count(*) or count(distinct item) depending on your semantic).
Once you sort these two things out, you can then use two subselects to get each scalar value and then convert them to strings as you are attempting to do in your example. Here's an example on how the pattern to do this should look once you clarify your data model issue
select convert(nvarchar(100),a) + '/' + convert(nvarchar(100),b) FROM
(
select (select count(*) from sys.objects) as a, (select count(*) from sys.objects) as b
) C
Result:
101/101
It's a fairly quick operation so for readability I'd try something like this. You can format your results easily at the bottom too, should you need to add dashes or extra spaces too.
DECLARE #A int,
#B int
SET #A = (
SELECT COUNT(*)
FROM [db].[table1]
WHERE [ItemType] = 2
)
SET #B = (
SELECT TOP(1) Items
FROM [db].[table2]
WHERE [ItemType] = 2
)
SELECT (#A + '/' + #B) as result

Avoid duplicate values in comma delimited sql query

hello I have here a comma delimited query:
select [Product_Name]
,(select h2.Location_name + ', ' from (select distinct * from [dbo].[Product_list]) h2 where h1.Product_Name = h2.Product_Name
order by h2.Product_Name for xml path ('')) as Location_name
,(select h2.[Store name] + ', ' from [dbo].[Product_list] h2 where h1.Product_Name = h2.Product_Name
order by h2.Product_Name for xml path ('')) as store_name, sum(Quantity) as Total_Quantity from [dbo].[Product_list] h1
group by [Product_Name]
but this query shows duplicated data in comma delimited form, my problem is how will I only show the distinct values of the column in comma delimited form? can anyone please help me?
Well, if you don't SELECT DISTINCT * FROM dbo.Product_list and instead SELECT DISTINCT location_name FROM dbo.Product_list, which is anyway the only column you need, it will return only distinct values.
T-SQL supports the use of the asterisk, or “star” character (*) to
substitute for an explicit column list. This will retrieve all columns
from the source table. While the asterisk is suitable for a quick
test, avoid using it in production work, as changes made to the table
will cause the query to retrieve all current columns in the table’s
current defined order. This could cause bugs or other failures in
reports or applications expecting a known number of columns returned
in a defined order. Furthermore, returning data that is not needed can
slow down your queries and cause performance issues if the source
table contains a large number of rows. By using an explicit column
list in your SELECT clause, you will always achieve the desired
results, providing the columns exist in the table. If a column is
dropped, you will receive an error that will help identify the problem
and fix your query.
Using SELECT DISTINCT will filter out duplicates in the result set.
SELECT DISTINCT specifies that the result set must contain only unique
rows. However, it is important to understand that the DISTINCT option
operates only on the set of columns returned by the SELECT clause. It
does not take into account any other unique columns in the source
table. DISTINCT also operates on all the columns in the SELECT list,
not just the first one.
From Querying Microsoft SQL Server 2012 MCT Manual.

Concatenation with a complex query - SQL Server

So I've got a query with multiple joins and several rows that I want to put on one line. A couple of PIVOT statements solved most of this problem, but I have one last field with multiple rows (User Names) that I want to concatenate into one column.
I've read about COALESCE and got a sample to work, but I did not know how to combine the variable returned with the other data fields, as it has no key.
I also saw this recommended approach:
SELECT [ID],
STUFF((
SELECT ', ' + CAST([Name] AS VARCHAR(MAX))
FROM #YourTable WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE
/* Use .value to uncomment XML entities e.g. > < etc*/
).value('.','VARCHAR(MAX)')
,1,2,'') as NameValues
FROM #YourTable Results
GROUP BY ID
But again, I'm not sure how to incorporate this into a complex query.
BTW, the users do not have write access to the DB, so cannot create functions, views, tables or even execute functions. So this limits the options somewhat.

Is it possible to avoid subquery in select when concatenating columns?

I have a "main" table containing an id (plus some other columns) and an aka table which joins to it by the [main id] column to main.id. The following query returns some columns from main along with a column of concatenated comma-separated "lastName"s from aka:
SELECT m.id, m.name,
(SELECT a.[lastname] + ',' AS [text()]
FROM aka a
WHERE a.[main id] = m.[id]
FOR xml path ('')) [akas]
FROM main m
This works fine, but I'd like to know if there is a way to avoid doing this in a subquery?
Using CROSS APPLY you could move subquery from SELECT list:
SELECT m.id, m.name,
(SELECT a.[lastname] + ',' AS [text()]
FROM aka a
WHERE a.[main id] = m.[id]
FOR xml path ('')) [akas]
FROM main m;
to:
SELECT m.id, m.name, s.akas
FROM main m
CROSS APPLY (SELECT a.[lastname] + ',' AS [text()]
FROM aka a
WHERE a.[main id] = m.[id]
FOR xml path ('')) AS s(akas)
Notes:
You could refer to s.akas multiple time
You could add WHERE s.akas ...
Long subquery in SELECT list could be less readable
If it is possbile that correlated subquery return no rows you need to use OUTER APPLY instead.
Generally spoken there's nothing against a sub-query in a technical view...
You might prefer an APPLY due to readability or multi reference.
Whenever you put a sub-query directly into the column's list like here:
SELECT Column1
,Column2
,(SELECT x FROM y) AS Column3
,[...]
... this sub-select must deliver
just one column
of just one row
Using FOR XML PATH(''),TYPE lets the result be one single value of type XML. This makes it possible to return many rows/columns "as one". Without the ,TYPE it will be the XML "as text". The concatenation trick with XML is possible due to a speciality of the generation of XML with empty tag names and return "as text". But in any case: The returned value will be just one bit of information, therefore fitting into a column list.
Whenever you expect more than one row, you'd have to force this to be one bit of data (like - often seen! - SELECT TOP 1 x FROM y ORDER BY SomeSortKey, which brings back the first or the last or ...)
All other intentions to get 1:n data needs 'JOIN' or 'APPLY'. With scalar data, as in your case, there will actually be no difference, whether you use a sub-select or an APPLY.
Since you have an arbitrary number of records combining for the final string, what you have is the best option to do this in SQL. Generally, you are expected to return one row per item, and if you want a CSV string then build that in your client code.

SQL Server Join columns with wildcard

I need to find a way to join two tables based using the columns' names with wild cards. This is the scenario I have:
CREATE TABLE #table1
(
AuthCode varchar(10)
)
INSERT INTO #table1
VALUES ('52201')
SELECT * FROM #table1
JOIN bilact
ON #table1.AuthCode = bilact.byAuthCode
In bilact table, the auth code is 005221 (the field is a varchar) and when I run the above query, it does not return any result.
The situation I am running into is so complicated and i do not want to explain why I must do it this way cause it is the only way that will work. Is there a way I can add the wild card with the column name in a join? Thank you.
Assuming you want one column to be the substring of another:
SELECT * FROM #table1
JOIN bilact
ON str(bilact.byAuthCode) LIKE '%' + #table1.AuthCode + '%'
or, if you need to match a substring in either table
SELECT DISTINCT * FROM #table1
JOIN bilact
ON str(bilact.byAuthCode) LIKE '%' + #table1.AuthCode + '%'
OR #table1.byAuthCode LIKE '%' + str(bilact.AuthCode) + '%'
You can remove the str()'s if both columns are varchars or some form of string field.
Otherwise, you need to decide what the longest common substring the 2 columns need in common, and in the join, iterate over every possible substring of one of the columns and do a like as above with the other column against it.

Resources