Group query rows result in one result - sql

I need make a query that I get the result and put in one line separated per comma.
For example, I have this query:
SELECT
SIGLA
FROM
LANGUAGES
This query return the result below:
SIGLA
ESP
EN
BRA
I need to get this result in one single line that way:
SIGLA
ESP,EN,BRA
Can anyone help me?
Thank you!

SELECT LISTAGG(SIGLA, ', ') WITHIN GROUP (ORDER BY SIGLA) " As "S_List" FROM LANGUAGES
Should be the listagg sequence you are needing

try
SELECT LISTAGG( SIGLA, ',' ) within group (order by SIGLA) as NewSigla FROM LANGUAGES

If you want to get the values grouped together in the order that Oracle produces the rows then:
SELECT LISTAGG( SIGLA, ',' ) WITHIN GROUP ( ORDER BY ROWNUM ) AS SIGLA
FROM LANGUAGES;
If you want alphabetical ordering then replace ORDER BY ROWNUM with ORDER BY SIGLA (or, curiously, ORDER BY NULL).

Related

SQL query to get the columns into one and comma delimited [duplicate]

Would it be possible to construct SQL to concatenate column values from
multiple rows?
The following is an example:
Table A
PID
A
B
C
Table B
PID SEQ Desc
A 1 Have
A 2 a nice
A 3 day.
B 1 Nice Work.
C 1 Yes
C 2 we can
C 3 do
C 4 this work!
Output of the SQL should be -
PID Desc
A Have a nice day.
B Nice Work.
C Yes we can do this work!
So basically the Desc column for out put table is a concatenation of the SEQ values from Table B?
Any help with the SQL?
There are a few ways depending on what version you have - see the oracle documentation on string aggregation techniques. A very common one is to use LISTAGG:
SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;
Then join to A to pick out the pids you want.
Note: Out of the box, LISTAGG only works correctly with VARCHAR2 columns.
There's also an XMLAGG function, which works on versions prior to 11.2. Because WM_CONCAT is undocumented and unsupported by Oracle, it's recommended not to use it in production system.
With XMLAGG you can do the following:
SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result"
FROM employee_names
What this does is
put the values of the ename column (concatenated with a comma) from the employee_names table in an xml element (with tag E)
extract the text of this
aggregate the xml (concatenate it)
call the resulting column "Result"
With SQL model clause:
SQL> select pid
2 , ltrim(sentence) sentence
3 from ( select pid
4 , seq
5 , sentence
6 from b
7 model
8 partition by (pid)
9 dimension by (seq)
10 measures (descr,cast(null as varchar2(100)) as sentence)
11 ( sentence[any] order by seq desc
12 = descr[cv()] || ' ' || sentence[cv()+1]
13 )
14 )
15 where seq = 1
16 /
P SENTENCE
- ---------------------------------------------------------------------------
A Have a nice day
B Nice Work.
C Yes we can do this work!
3 rows selected.
I wrote about this here. And if you follow the link to the OTN-thread you will find some more, including a performance comparison.
The LISTAGG analytic function was introduced in Oracle 11g Release 2, making it very easy to aggregate strings.
If you are using 11g Release 2 you should use this function for string aggregation.
Please refer below url for more information about string concatenation.
http://www.oracle-base.com/articles/misc/StringAggregationTechniques.php
String Concatenation
As most of the answers suggest, LISTAGG is the obvious option. However, one annoying aspect with LISTAGG is that if the total length of concatenated string exceeds 4000 characters( limit for VARCHAR2 in SQL ), the below error is thrown, which is difficult to manage in Oracle versions upto 12.1
ORA-01489: result of string concatenation is too long
A new feature added in 12cR2 is the ON OVERFLOW clause of LISTAGG.
The query including this clause would look like:
SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;
The above will restrict the output to 4000 characters but will not throw the ORA-01489 error.
These are some of the additional options of ON OVERFLOW clause:
ON OVERFLOW TRUNCATE 'Contd..' : This will display 'Contd..' at
the end of string (Default is ... )
ON OVERFLOW TRUNCATE '' : This will display the 4000 characters
without any terminating string.
ON OVERFLOW TRUNCATE WITH COUNT : This will display the total
number of characters at the end after the terminating characters.
Eg:- '...(5512)'
ON OVERFLOW ERROR : If you expect the LISTAGG to fail with the
ORA-01489 error ( Which is default anyway ).
For those who must solve this problem using Oracle 9i (or earlier), you will probably need to use SYS_CONNECT_BY_PATH, since LISTAGG is not available.
To answer the OP, the following query will display the PID from Table A and concatenate all the DESC columns from Table B:
SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
FROM (
SELECT a.pid, seq, description
FROM table_a a, table_b b
WHERE a.pid = b.pid(+)
)
)
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;
There may also be instances where keys and values are all contained in one table. The following query can be used where there is no Table A, and only Table B exists:
SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
FROM (
SELECT pid, seq, description
FROM table_b
)
)
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;
All values can be reordered as desired. Individual concatenated descriptions can be reordered in the PARTITION BY clause, and the list of PIDs can be reordered in the final ORDER BY clause.
Alternately: there may be times when you want to concatenate all the values from an entire table into one row.
The key idea here is using an artificial value for the group of descriptions to be concatenated.
In the following query, the constant string '1' is used, but any value will work:
SELECT SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
SELECT ROW_NUMBER () OVER (PARTITION BY unique_id ORDER BY pid, seq) rnum, description
FROM (
SELECT '1' unique_id, b.pid, b.seq, b.description
FROM table_b b
)
)
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1;
Individual concatenated descriptions can be reordered in the PARTITION BY clause.
Several other answers on this page have also mentioned this extremely helpful reference:
https://oracle-base.com/articles/misc/string-aggregation-techniques
LISTAGG delivers the best performance if sorting is a must(00:00:05.85)
SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;
COLLECT delivers the best performance if sorting is not needed(00:00:02.90):
SELECT pid, TO_STRING(CAST(COLLECT(Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;
COLLECT with ordering is bit slower(00:00:07.08):
SELECT pid, TO_STRING(CAST(COLLECT(Desc ORDER BY Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;
All other techniques were slower.
Before you run a select query, run this:
SET SERVEROUT ON SIZE 6000
SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER"
FROM SUPPLIERS;
Try this code:
SELECT XMLAGG(XMLELEMENT(E,fieldname||',')).EXTRACT('//text()') "FieldNames"
FROM FIELD_MASTER
WHERE FIELD_ID > 10 AND FIELD_AREA != 'NEBRASKA';
In the select where you want your concatenation, call a SQL function.
For example:
select PID, dbo.MyConcat(PID)
from TableA;
Then for the SQL function:
Function MyConcat(#PID varchar(10))
returns varchar(1000)
as
begin
declare #x varchar(1000);
select #x = isnull(#x +',', #x, #x +',') + Desc
from TableB
where PID = #PID;
return #x;
end
The Function Header syntax might be wrong, but the principle does work.

BigQuery from column to rows by separator

I want to generate table and add all values per distinct id to one row using BigQuery
Example:
id label
000756f4-1af2-439b-b607-ce7384a6b8ee fast
000756f4-1af2-439b-b607-ce7384a6b8ee streaming
000756f4-1af2-439b-b607-ce7384a6b8ee other
0007bac4-1bed-4bf0-8b55-d21216723ef5 issue
000a03d2-f88c-4150-aa96-40b9fdaccb17 fast
000a03d2-f88c-4150-aa96-40b9fdaccb17 other
I would like to receive such table:
id label
000756f4-1af2-439b-b607-ce7384a6b8ee fast, streaming, other
0007bac4-1bed-4bf0-8b55-d21216723ef5 issue
000a03d2-f88c-4150-aa96-40b9fdaccb17 fast, other
Is it possible to achieve it with BigQuery?
You can just use string_agg():
select id, string_agg(label, ', ') as labels
from t
group by id;
Note that the ordering is arbitrary (and might even vary from one run to another). You might want to include an order by as well:
select id, string_agg(label, ', ' order by label) as labels
from t
group by id;
Update
Use string_agg:
select id, string_agg(label, ', ')
from mytable
group by id
Original answer
Use array_agg and array_to_string:
select id, array_to_string(array_agg(label), ', ')
from mytable
group by id

How do I get the top record from listagg in Oracle?

I am trying to get the first record from the list return by listagg(). I have tried to use LIMIT, TOP, PARTITION but nothing is working for me.
The query is something like this:
select distinct(col1), col2, LISTAGG(col4, '; ')
WITHIN GROUP (ORDER BY....)
from table1 inner join table 2 on ..
group by ....;
So, the query was to get all the records that are satisfying the conditions. Now, since I have the list, I need to provide the fix only for the latest record only.
Hope I am clear with what I am doing. Any help is appreciated.
Why would you use listagg() for this? Use keep:
SELECT MAX(COL4) KEEP (DENSE_RANK FIRST col4 ORDER BY . . .)
Or, without the GROUP BY, you can just use the analytic function FIRST_VALUE().
Use SUBSTR and INSTR :
SELECT t.*,
SUBSTR(YourColumn, 0, INSTR(YourColumn, ',')-1) as newCol
FROM (Your Query Here) t
That is only if col4 doesn't contain a comma.
Well, you don't need LISTAGG for the first case. It is less efficient to extract from output of LISTAGG. Instead you can use FIRST_VALUE. For the second case you can use row_number() function and aplply LISTAGG on top as shown.
For selecting latest record use
SELECT t.*, FIRST_VALUE(col4) OVER( PARTITION BY id ORDER BY ..) latest
FROM
( select id,col1,..col4 FROM.. ) t;
For selecting elements apart from the latest as LISTAGG, use
select id,LISTAGG(col4 ,';') WITHIN GROUP ( ORDER BY .. ) apart_from_latest FROM
(
SELECT t.*, row_number() OVER( PARTITION BY id ORDER BY ..) rn
FROM
( select id,col1,..col4 FROM.. ) t ) WHERE rn > 1 group BY ..;

Get only 1 occurrence for the duplicate records

select DISTINCT a.Schooldistricttown, a.Schooldistrictnum
from [Legacy].[dbo].[MyTables] as a
It returns :
Can you tell me how to get only one occurrence of a.Schooldistricttown?
I have tried with DISTINCT and GROUP BY. But it's not working.
Note : I need to show both columns also.
Two options, if it doesn't matter which value you get in Schooldistrictnum then group by with MAX()/MIN() will solve this:
SELECT a.Schooldistricttown,MAX(a.Schooldistrictnum)
from [Legacy].[dbo].[MyTables] a
GROUP BY a.Schooldistricttown
If you do care, use ROW_NUMBER() :
SELECT s.Schooldistricttown,s.Schooldistrictnum
FROM (
SELECT a.Schooldistricttown,a.Schooldistrictnum,
ROW_NUMBER() OVER(PARTITION BY a.Schooldistricttown ORDER BY a.<ORDER_COLUMN>) as rnk
from [Legacy].[dbo].[MyTables] a) s
WHERE s.rnk = 1
You need to replace <ORDER_COLUMN> with the actual column that you decide which value you want by it
If you want all Schooldistrictnum, use this
SELECT DISTINCT
a.Schooldistricttown,
(
SELECT DISTINCT
ina.Schooldistrictnum + ', ' AS [text()]
FROM
[Legacy].[dbo].[MyTables] as ina
WHERE
ina.Schooldistricttown = a.Schooldistricttown
FOR XML PATH ('')
) AS Schooldistrictnum
FROM [Legacy].[dbo].[MyTables] as a

use Row_number after applying distinct

I am creating an SP which gives some result by applying distinct on it, now I want to implement sever side paging, so I tried using Row_number on distinct result like:
WITH CTE AS
(
SELECT ROW_NUMBER() OVER(ORDER BY tblA.TeamName DESC)
as Row,tblA.TeamId,tblA.TeamName,tblA.CompId,tblA.CompName,tblA.Title,tblA.Thumbnail,tblA.Rank,tblA.CountryId,tblA.CountryName
FROM
(
--The table query starts with SELECT
)tblA
)
SELECT CTE.* FROM CTE
WHERE CTE.Row BETWEEN #StartRowIndex AND #StartRowIndex+#NumRows-1
ORDER BY CTE.CountryName
but rows are first assigned RowNumber then distinct get applied that is why I am getting duplicate values, how to get distinct rows first then get row numbers for the same.
Any solution on this? Am I missing something?
need answer ASAP.
thanks in advance!
Don't you need to add "partition by" to your ROW_NUMBER statement?
ROW_NUMBER() OVER(Partition by ___, ___, ORDER BY tblA.TeamName DESC)
In the blank spaces, place the column names you would like to create a new row number for. Duplicates will receive a number that is NOT 1 so you might not need the distinct.
To gather the unique values you could write a subquery where the stored procedure only grabs the rows with a 1 in them.
select * from
(
your code
) where row = 1
Hope that helps.
I'm not sure why you're doing this:
WHERE CTE.Row BETWEEN #StartRowIndex AND #StartRowIndex+#NumRows-1