SQL to concatenate all Query rows into one row - sql

I have seen the following questions but can't get them to work for what I'm looking for.
How can multiple rows be concatenated into one in Oracle without creating a stored procedure?
SQL Query to concatenate column values from multiple rows in Oracle
I am using Oracle and would like the solution not to have pl-Sql.
I'm trying to get the query to look as so with a possible new line per each row and delimited with a | or comma :
Data Set:
question_id element_message date
--------------------------------------------
1 hello 10/10/19
2 goodbye 11/11/19
Desired one row query result:
1 | hello | 10/10/19 --new line added in query(if possible)
2 | goodbye| 11/11/19
And if new line in query isn't possible:
Desired result:
1 | hello | 10/10/19 | 2 | goodbye| 11/11/19 |
*EDIT
To to clarify why I am using this. I am using a tool called Geneos with has a row by row limitation in terms of triggering an email. So this is a hack to trigger one email for all the rows per that day.

Why do you want a one row query if you want each row to be printed on a different line?
If you just wan to run a query that returns your data with no header or page separator use this:
set pagesize 0
select question_id || '|' || element_message || '|' || date from your_table;

SQL Server solution :You can solve it using self join like below:
select top 1 CONCAT(d1.question_id, ' | ', d1.element_message, ' | ', d1.date,
'|' d2.question_id, ' | ', d2.element_message, ' | ', d2.date)
from data d1 join data d2 on (d1.question_id < d2.question_id and)

Is this what you're looking for?
SELECT CONCAT(question_id, ' | ', element_message, ' | ', date) FROM Data

Related

Concatenate multiple rows to form one single row in SQL Server?

Overview
I need to build a description field that describes an entity. The data I am working with has the property description split for each individual key in my table. Below is an example of what the data looks like:
+------------+--------------------+----------+
| Key | Desc | Order_Id |
+------------+--------------------+----------+
| 5962417474 | Big Yellow Door | 14775 |
| 5962417474 | Orange Windows | 14776 |
| 5962417474 | Blue Triangle Roof | 14777 |
+------------+--------------------+----------+
Originally, I wrote a query using an aggregate function like so:
SELECT
[P].[KEY],
CONCAT (MIN([P].[Desc]), + ' ' + MAX([P].[Desc])) [PROPERTY_DESCRIPTION]
FROM [dbo].[PROP_DESC] [P]
WHERE [P].[KEY] = '5962417474'
GROUP BY [P].[KEY];
This worked great for two row entries but then I realized what if I have multiple records for a property description? So I wrote the following query to check if I had multiple property descriptions:
SELECT
[P].[KEY], COUNT([P].[KEY])
FROM [dbo].[PROP_DESC] [P]
GROUP BY [P].[KEY]
HAVING COUNT(*) > 2; -- Returns one record which is the above table result.
This gave me back a record with three descriptions so my original query will not work. How can I tackle this problem down when there are multiple fields?
Desired Output
+------------+---------------------------------------------------+----------+
| Key | Desc | Order_Id |
+------------+---------------------------------------------------+----------+
| 5962417474 | Big Yellow Door Orange Windows Blue Triangle Roof | 14775 |
+------------+---------------------------------------------------+----------+
It depends on what SQL language you're using, but you'll want to use some kind of group concat / array agg function. Eg:
SELECT
Key,
STRING_AGG(desc, ', ')
FROM TABLE
GROUP BY Key;
I have solved my problem with the following query for those that have the same problem and do not have access to STRING_AGG which is introduced in SQL Server 2017:
SELECT
[P].[KEY],
[PROPERTY_DESCRIPTION] = STUFF((
SELECT ' ' + [P2].[DESC]
FROM [dbo].[PROP_DESC] [P2]
WHERE [P].[KEY] = [P2].[KEY]
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 1, '')
FROM [dbo].[PROP_DESC] [P]
WHERE [P].[KEY] = '5962417474'
GROUP BY [P].[KEY]
There are many ways to do it in SQL server:
Below is one way:
SELECT key
,STUFF((SELECT '| ' + CAST(prop_desc AS VARCHAR(MAX)) [text()]
FROM PROP_DESC
WHERE key = t.key
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') prop_desc
FROM PROP_DESC t
GROUP BY key

How to write SQL queries with respect to the following conditions?

I have a database table in which a column tags contain values such as:
"AutoMNRP, MNRP"
"Macro, MNRP"
"AutoMNRP, Micro"
"Macro, Micro"
where "...." represents a string.
I want to write a SQL query such that it filters out all results having MNRP tag in it. How can I do this?
I tried a not like operator of SQL on it, but if I want to remove MNRP tag, it also filters out AutoMNRP tag.
At the last of query I need results featuring -
"AutoMNRP, Micro"
"Macro, Micro".
(Results when MNRP is filtered out.)
The right answer to this is to fix your design, you shouldn't store the data like this (comma separated), because your table should be like (and the duplicates should be removed and handled too)
+----------+
| Data |
+----------+
| AutoMNRP |
| MNRP |
| Macro |
| MNRP |
| AutoMNRP |
| Micro |
| Macro |
| Micro |
+----------+
But... here is a way it may fit you requirements
;WITH T(Str) AS
(
SELECT 'AutoMNRP, MNRP' UNION ALL
SELECT 'Macro, MNRP' UNION ALL
SELECT 'AutoMNRP, Micro' UNION ALL
SELECT 'Macro, Micro'
)
SELECT Str
FROM T
WHERE Str NOT LIKE '% MNRP,%'
AND
Str NOT LIKE '%, MNRP';
Returns:
+-----------------+
| Str |
+-----------------+
| AutoMNRP, Micro |
| Macro, Micro |
+-----------------+
Live Demo
You also (as Larnu point to) do as
;WITH T(Str) AS
(
SELECT 'AutoMNRP, MNRP' UNION ALL
SELECT 'Macro, MNRP' UNION ALL
SELECT 'AutoMNRP, Micro' UNION ALL
SELECT 'Macro, Micro'
)
SELECT Str
FROM T
WHERE CONCAT(', ', Str, ',') NOT LIKE '%, MNRP,';
In SQL Server 2016+ you can use the STRING_SPLIT function. So you can multiply a record by the number of separated values in the tags column so you can then apply a simple WHERE clause. Something like this:
WITH cte AS
(
SELECT Id, SingleTag
FROM table_name CROSS APPLY STRING_SPLIT(tags, ',')
)
SELECT * FROM cte WHERE SingleTag = 'MNRP'

Query with conditions on multiple value column

I am building report in Oracle Apex 4.2. Table that report is build on has multiple values inside one of the columns.
-----------------------------------
| ID | NAME | PROJECT_ID |
-----------------------------------
| 1 | P1 | 23:45:56 |
| 2 | P2 | 23 |
| 3 | P3 | 45:65 |
-----------------------------------
I would like to build a query to retrieve names based on project_id's.
Select name from table where project_id = 23;
This obviously will return P2 only however I would like to build a query which would return P1 and P2 if we searched for 23.
Any help greatly appreciated.
You can use LIKE instead of = :
Select name from table where project_id LIKE '%23%';
If you've got a common delimiter such as the ':' in your example you could use the following to exclude results like '123':
SELECT name FROM table WHERE ':' || project_id || ':' LIKE '%:23:%'
By concatenating the delimiter to the front and back of the string, you don't have to write multiple criteria: LIKE '23:%' OR LIKE '%:23:%' OR LIKE '%:23' to handle the first and last number in the list.
This is a common design in Apex due to its builtin support for colon-delimited strings (e.g. to drive shuttle controls and other item types).
I generally use this pattern:
Select name from table where INSTR(':'||project_id||':',':23:') > 0;
P.S. It's a pity about that column name - I would have called it something like PROJECT_ID_LIST.

SQL Server Best match query with update (T-SQL)

I am trying to find out what's the most optimized SQL Query to achieve the following.
I have a table containing ZipCodes/PostalCodes, let's assume the following structure:
table_codes:
ID | ZipCode
---------------
1 1234
2 1235
3 456
and so on.
The users of my application fill up a profile where they are required to enter their ZipCode (PostalCode).
Assuming that sometimes, the user will enter a ZipCode not defined in my table, I am trying to suggest a Best Match based on the zip entered by the user.
I am using the following query:
Declare #entered_zipcode varchar(10)
set #entered_zipcode = '23456'
SELECT TOP 1 table_codes.ZipCode
FROM table_codes
where #entered_zipcode LIKE table_codes.ZipCode + '%'
or table_codes.ZipCode + '%' like #entered_zipcode + '%'
ORDER BY table_codes.ZipCode, LEN(table_codes.ZipCode) DESC
Basically, I am trying the following:
if the #entered_zipcode is longer than any zip code in the table, I am trying to get to get the best prefix in the zip table matching the #entered_zipcode
if the #entered_zipcode is shorter than any existing code in the table, I am trying to use it as a prefix and get the best match in the table
Moreover, I am building a temp table with the following structure:
#tmpTable
------------------------------------------------------------------------------------
ID | user1_enteredzip | user1_bestmatchzip | user2_enteredzip | user2_bestmatchzip |
------------------------------------------------------------------------------------
1 | 12 | *1234* | 4567 | **456** |
2 |
3 |
4 |
Entered zip is the one the user enters and the code between * .. * is the best matching code from my lookup table, that I am trying to get using the query below.
The query seems to take a little bit to long and this is why I am asking for help in optimizing it:
update #tmpTable
set user1_bestmatchzip = ( SELECT TOP 1
zipcode
FROM table_codes
where #tmpTable.user1_enteredzip LIKE table_codes.zipcode + '%'
or table_codes.zipcode + '%' like #tmpTable.user1_enteredzip + '%'
ORDER BY table_codes.zipcode, LEN(table_codes.zipcode) DESC
),
user2_bestmatchzip = ( SELECT TOP 1
zipcode
FROM table_codes
where #tmpTable.user2_enteredzip LIKE table_codes.zipcode + '%'
or table_codes.zipcode + '%' like #tmpTable.user2_enteredzip + '%'
ORDER BY table_codes.zipcode, LEN(table_codes.zipcode) DESC
)
from #tmpTable
What if you change your temp table to be like:
id | user | enteredzip | bestmatchzip
10 | 1 | 12345 | 12345
20 | 2 | 12 | 12345
That is: use a column to save the user number (1 or 2). This way you will update one row at a time.
Also, the ORDER BY takes time, did you set indices on the zipcode? Couldn't you create a field "length" in the zipcodes table to pre-compute the zipcodes lenghts?
EDIT:
I was thinking that ordering by LEN makes no sense, you could remove that! If the zipcodes cannot have duplicates, then ordering by the zipcode is just enought. If they can though, the LEN will always be equal!
You are comparing first characters of both strings - what if you compare substrings of minimal length?
select top 1 zipcode
from table_zipcodes
where substring(zipcode, 1, case when len(zipcode) > len (#entered_zipcode) then len(#entered_zipcode) else len (zipcode) end)
= substring (#entered_zipcode, 1, case when len(zipcode) > len (#entered_zipcode) then len(#entered_zipcode) else len (zipcode) end)
order by len (zipcode) desc
This will remove OR and allow for usage of index *in_#entered_zipcode LIKE table_codes.ZipCode + '%'*. Also, it seems to me that the ordering of results is wrong - shorter zipcodes go first.

How do you concat multiple rows into one column in SQL Server?

I've searched high and low for the answer to this, but I can't figure it out. I'm relatively new to SQL Server and don't quite have the syntax down yet. I have this datastructure (simplified):
Table "Users" | Table "Tags":
UserID UserName | TagID UserID PhotoID
1 Bob | 1 1 1
2 Bill | 2 2 1
3 Jane | 3 3 1
4 Sam | 4 2 2
-----------------------------------------------------
Table "Photos": | Table "Albums":
PhotoID UserID AlbumID | AlbumID UserID
1 1 1 | 1 1
2 1 1 | 2 3
3 1 1 | 3 2
4 3 2 |
5 3 2 |
I'm looking for a way to get the all the photo info (easy) plus all the tags for that photo concatenated like CONCAT(username, ', ') AS Tags of course with the last comma removed. I'm having a bear of a time trying to do this. I've tried the method in this article but I get an error when I try to run the query saying that I can't use DECLARE statements... do you guys have any idea how this can be done? I'm using VS08 and whatever DB is installed in it (I normally use MySQL so I don't know what flavor of DB this really is... it's an .mdf file?)
Ok, I feel like I need to jump in to comment about How do you concat multiple rows into one column in SQL Server? and provide a more preferred answer.
I'm really sorry, but using scalar-valued functions like this will kill performance. Just open SQL Profiler and have a look at what's going on when you use a scalar-function that calls a table.
Also, the "update a variable" technique for concatenation is not encouraged, as that functionality might not continue in future versions.
The preferred way of doing string concatenation to use FOR XML PATH instead.
select
stuff((select ', ' + t.tag from tags t where t.photoid = p.photoid order by tag for xml path('')),1,2,'') as taglist
,*
from photos
order by photoid;
For examples of how FOR XML PATH works, consider the following, imagining that you have a table with two fields called 'id' and 'name'
SELECT id, name
FROM table
order by name
FOR XML PATH('item'),root('itemlist')
;
Gives:
<itemlist><item><id>2</id><name>Aardvark</a></item><item><id>1</id><name>Zebra</name></item></itemlist>
But if you leave out the ROOT, you get something slightly different:
SELECT id, name
FROM table
order by name
FOR XML PATH('item')
;
<item><id>2</id><name>Aardvark</a></item><item><id>1</id><name>Zebra</name></item>
And if you put an empty PATH string, you get even closer to ordinary string concatenation:
SELECT id, name
FROM table
order by name
FOR XML PATH('')
;
<id>2</id><name>Aardvark</a><id>1</id><name>Zebra</name>
Now comes the really tricky bit... If you name a column starting with an # sign, it becomes an attribute, and if a column doesn't have a name (or you call it [*]), then it leaves out that tag too:
SELECT ',' + name
FROM table
order by name
FOR XML PATH('')
;
,Aardvark,Zebra
Now finally, to strip the leading comma, the STUFF command comes in. STUFF(s,x,n,s2) pulls out n characters of s, starting at position x. In their place, it puts s2. So:
SELECT STUFF('abcde',2,3,'123456');
gives:
a123456e
So now have a look at my query above for your taglist.
select
stuff((select ', ' + t.tag from tags t where t.photoid = p.photoid order by tag for xml path('')),1,2,'') as taglist
,*
from photos
order by photoid;
For each photo, I have a subquery which grabs the tags and concatenates them (in order) with a commma and a space. Then I surround that subquery in a stuff command to strip the leading comma and space.
I apologise for any typos - I haven't actually created the tables on my own machine to test this.
Rob
I'd create a UDF:
create function GetTags(PhotoID int) returns #tags varchar(max)
as
begin
declare #mytags varchar(max)
set #mytags = ''
select #mytags = #mytags + ', ' + tag from tags where photoid = #photoid
return substring(#mytags, 3, 8000)
end
Then, all you have to do is:
select GetTags(photoID) as tagList from photos
Street_Name ; Street_Code
west | 14
east | 7
west+east | 714
If want to show two different row concat itself , how can do it?
(I mean last row i want to show from select result. My table had first and secord record)