Combine multiple value into 1 for Impala SQL - sql

I want to combine multiple product entries into 1 and also sum their price. Currently, the database looks like this :
Name Product Price
Zack Vanilla Twist 1
Jane Lolipop 0.5
Zack Lolipop 0.5
Zack Candymint 0.5
Jane ChocoLoco LM 1.5
I want to change the look of this into something like this:
Name Product sum(Price)
Zack Vanilla Twist, Lolipop, Candymint 2
Jane Lolipop, ChocoLoco LM 2
How to do this using Impala SQL?

This query works for MySQL, this might help you.
select Name, group_concat(`product` separator ', ') Product, sum(Price)
from tempt
group by Name
order by Name desc
dbfiddle here

declare #temp table (Name varchar(50), product varchar(50), Price decimal(3,1))
insert into #temp values ('Zack','Vanilla Twist',1)
insert into #temp values ('Jane','Lolipop',0.5)
insert into #temp values ('Zack','Lolipop',0.5)
insert into #temp values ('Zack','Candymint',0.5)
insert into #temp values ('Jane','ChocoLoco LM',1.5)
-- No cursor, Whil loop, or User defined function:
SELECT
Name,
STUFF((
SELECT ', ' + product
FROM #temp
WHERE (name = Results.name)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS Product
,sum(Price) as [Sum(Price)]
FROM #temp Results
GROUP BY name
Output:
Name Product Sum(Price)
Jane Lolipop, ChocoLoco LM 2
Zack Vanilla Twist, Lolipop, Candymint 2

Related

Transform rows to comma delimited string by groups of n records

Based on this sample table
ID
NAME
ZIP
NTID
1
Juan
123
H1
1
Juan
123
H2
2
John
456
H3
2
John
456
H4
2
John
456
H5
I want to show ntid as a comma separated value but in groups of maximum 2 items
Expected result
ID
NAME
ZIP
NTID
1
Juan
123
H1, H2
2
John
456
H3, H4
2
John
456
H5
Using SQL Server, is there a way to achieve this?
By using the STRING_AGG function it concatenates all in a single row, but I need to group as groups of maximum of 2 members.
SELECT ID, NAME, ZIP, STRING_AGG(NTID,',')
FROM MyTable
GROUP BY ID, NAME, ZIP
So you can use a modified row number (divided by 2) to group your data into blocks of 2 rows, and then you can use string_agg().
You do need a way to order the rows though (if you want consistent results), I have assumed NTID will work, but you may have a better column to order by.
declare #Test table (ID int, [NAME] varchar(32), ZIP varchar(12), NTID varchar(2));
insert into #Test (ID, [NAME], ZIP, NTID)
values
(1, 'Juan', '123', 'H1'),
(1, 'Juan', '123', 'H2'),
(2, 'John', '456', 'H3'),
(2, 'John', '456', 'H4'),
(2, 'John', '456', 'H5');
with cte as (
select *
, (row_number() over (partition by ID order by NTID) - 1) / 2 rn
from #Test
)
select ID, [NAME], string_agg(NTID,',')
from cte
group by ID, [NAME], rn;
db<>fiddle here
Note: If you supply the DDL+DML as part of your question you make it much easier to answer.

Group columns that don't exactly match in sql server

I am working on a query to pull a list of Merchants and get a count of transactions for that merchant. Here's an example (Note: My table has more columns for description, location, status, amount, date, etc, but these are the important ones).
TransactionID MerchantName
1 MERCHANTA #123
2 MERCHANTA #541
3 MERCHANTA #456
4 MERCHANTB #123
5 MERCHANTB
6 SOME MERCHANTC #123
Now, I want to group these merchants together but since each merchant could have more than one store, their merchant name doesn't always match the same as other transactions.
The only way I know to group them together is the following standard query, but it's never going to work for the different store numbers.
SELECT MerchantName, COUNT(*)
FROM Transactions
GROUP BY MerchantName
My goal is to use Regex to replace the store number with a wildcard or blank string so I can group them together by merchant, regardless of store numbers. Here is my pattern: [#*]\s?[a-zA-Z\d]?
Expected output:
MerchantName TransactionCount
MERCHANTA 3
MERCHANTB 2
SOME MERCHANTC 1
Is this even possible? If so, what is a good way of doing this? Thanks in advance.
Just another option.
No need for the IIF() or a CASE. We just add a "fail-safe" in the charindex()
Example
Declare #YourTable Table ([TransactionID] int,[MerchantName] varchar(50))
Insert Into #YourTable Values
(1,'MERCHANTA #123')
,(2,'MERCHANTA #541')
,(3,'MERCHANTA#456') -- << made ugly
,(4,' MERCHANTB #123') -- << made ugly
,(5,'MERCHANTB')
,(6,'SOME MERCHANTC #123')
Select [MerchantName]
,TransCount = count(*)
From (
Select [MerchantName] = ltriM(rtrim(left([MerchantName],charindex('#',[MerchantName]+'#')-1)))
From #YourTable
) A
Group By [MerchantName]
Returns
MerchantName TransCount
MERCHANTA 3
MERCHANTB 2
SOME MERCHANTC 1
> EDIT for the *
...
Select [MerchantName] = ltriM(rtrim(left([MerchantName],charindex('#',replace([MerchantName],'*','#')+'#')-1)))
From #YourTable
...
Consider:
with cte as (
select
TransactionID,
iif(
charindex(' #', MerchantName) > 0,
left(MerchantName, charindex(' #', MerchantName) - 1),
MerchantName
) MerchantName
from mytable
)
select MerchantName, count(*) TransactionCount
from cte
group by MerchantName
In the common table expression, we modify the merchant name by removing everything that is after ' #' (a space, then the hash sign). Then all that is left to do is aggregate.
Demo on DB Fiddle:
MerchantName | TransactionCount
:------------- | ---------------:
MERCHANTA | 3
MERCHANTB | 2
SOME MERCHANTC | 1
Note: this assumes that ' #' always represents the splitting pattern.
Using stuff and Patindex
DECLARE #MYTAB AS TABLE(transactionId int IDENTITY(1,1),MerchantName nvarchar(50))
insert into #MYTAB(MerchantName) values('MERCHANTA #123')
insert into #MYTAB(MerchantName) values('MERCHANTA #541')
insert into #MYTAB(MerchantName) values('MERCHANTA #456')
insert into #MYTAB(MerchantName) values('MERCHANTB #123')
insert into #MYTAB(MerchantName) values('MERCHANTB')
insert into #MYTAB(MerchantName) values('SOME MERCHANTC #123')
;with cte as(
select
case
when stuff(MerchantName,patindex('%#%',MerchantName),4,'') is not null then
stuff(MerchantName,patindex('%#%',MerchantName),4,'') else MerchantName end [customer] from #MYTAB )
select [customer],count(1) transactionCount from cte group by [customer]

Sum records and add note what was summed up in sql

I have a simple table looks like this one:
company_Id user_Id price sub_price
123456 11111 200 NULL
123456 11111 500 NULL
456789 22222 300 NULL
And I want to consolidate records which has count(*) >= 2 into one row by summing up the price but with note what was summed up in column sub_price. Desired output should look like this one:
company_Id user_Id price sub_price
123456 11111 700 200,500
456789 22222 300 300
Is there any simple approach how to achieve desired output? Many thanks for your help in advance.
You can use listagg to turn the elements of a group into a string:
SELECT ...
, LISTAGG(price, ',') WITHIN GROUP (ORDER BY price) sub_price
FROM ...
Although listagg is SQL standard, it is not yet supported by all databases. However, most database offer similar functionality by a different name—e.g. string_agg in PostgreSQL and SQL Sever (since 2017) or group_concat in MySQL.
More info: http://modern-sql.com/feature/listagg (also showing alternatives if listagg is not supported)
This is one possible solution;
More info about concatenating multiple rows into single row you can find here
DECALRE #tbl AS table (
company_Id int
,user_Id int
,price int
,sub_price varchar(25)
)
INSERT INTO #tbl values (123456, 11111, 200, NULL)
INSERT INTO #tbl values (123456, 11111, 500, NULL)
INSERT INTO #tbl values (456789, 22222, 300, NULL)
SELECT
company_Id
,user_Id
,SUM(price) AS price
,STUFF(
(SELECT ',' + cast(price as varchar)
FROM #tbl
WHERE company_Id = a.company_id
AND user_Id = a.user_Id
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,'') AS sub_price
FROM #tbl a
GROUP BY company_Id, user_Id

sql query db2 9.7 [duplicate]

Is there a built in function for comma separated column values in DB2 SQL?
Example: If there are columns with an ID and it has 3 rows with the same ID but have three different roles, the data should be concatenated with a comma.
ID | Role
------------
4555 | 2
4555 | 3
4555 | 4
The output should look like the following, per row:
4555 2,3,4
LISTAGG function is new function in DB2 LUW 9.7
see example:
create table myTable (id int, category int);
insert into myTable values (1, 1);
insert into myTable values (2, 2);
insert into myTable values (5, 1);
insert into myTable values (3, 1);
insert into myTable values (4, 2);
example: select without any order in grouped column
select category, LISTAGG(id, ', ') as ids from myTable group by category;
result:
CATEGORY IDS
--------- -----
1 1, 5, 3
2 2, 4
example: select with order by clause in grouped column
select
category,
LISTAGG(id, ', ') WITHIN GROUP(ORDER BY id ASC) as ids
from myTable
group by category;
result:
CATEGORY IDS
--------- -----
1 1, 3, 5
2 2, 4
I think with this smaller query, you can do what you want.
This is equivalent of MySQL's GROUP_CONCAT in DB2.
SELECT
NUM,
SUBSTR(xmlserialize(xmlagg(xmltext(CONCAT( ', ',ROLES))) as VARCHAR(1024)), 3) as ROLES
FROM mytable
GROUP BY NUM;
This will output something like:
NUM ROLES
---- -------------
1 111, 333, 555
2 222, 444
assumming your original result was something like that:
NUM ROLES
---- ---------
1 111
2 222
1 333
2 444
1 555
Depending of the DB2 version you have, you can use XML functions to achieve this.
Example table with some data
create table myTable (id int, category int);
insert into myTable values (1, 1);
insert into myTable values (2, 2);
insert into myTable values (3, 1);
insert into myTable values (4, 2);
insert into myTable values (5, 1);
Aggregate results using xml functions
select category,
xmlserialize(XMLAGG(XMLELEMENT(NAME "x", id) ) as varchar(1000)) as ids
from myTable
group by category;
results:
CATEGORY IDS
-------- ------------------------
1 <x>1</x><x>3</x><x>5</x>
2 <x>2</x><x>4</x>
Use replace to make the result look better
select category,
replace(
replace(
replace(
xmlserialize(XMLAGG(XMLELEMENT(NAME "x", id) ) as varchar(1000))
, '</x><x>', ',')
, '<x>', '')
, '</x>', '') as ids
from myTable
group by category;
Cleaned result
CATEGORY IDS
-------- -----
1 1,3,5
2 2,4
Just saw a better solution using XMLTEXT instead of XMLELEMENT here.
Since DB2 9.7.5 there is a function for that:
LISTAGG(colname, separator)
check this for more information: Using LISTAGG to Turn Rows of Data into a Comma Separated List
My problem was to transpose row fields(CLOB) to column(VARCHAR) with a CSV and use the transposed table for reporting. Because transposing on report layer slows down the report.
One way to go is to use recursive SQL. You can find many articles about that but its difficult and resource consuming if you want to join all your recursive transposed columns.
I created multiple global temp tables where I stored single transposed columns with one key identifier. Eventually, I had 6 temp tables for joining 6 columns but due to limited resource allocation I wasnt able to bring all columns together. I opted to below 3 formulas and then I just had to run 1 query which gave me output in 10 seconds.
I found various articles on using XML2CLOB functions and have found 3 different ways.
REPLACE(VARCHAR(XML2CLOB(XMLAGG(XMLELEMENT(NAME "A",ALIASNAME.ATTRIBUTENAME)))),'', ',') AS TRANSPOSED_OUTPUT
NVL(TRIM(',' FROM REPLACE(REPLACE(REPLACE(CAST(XML2CLOB(XMLAGG(XMLELEMENT(NAME "E", ALIASNAME.ATTRIBUTENAME))) AS VARCHAR(100)),'',' '),'',','), '', 'Nothing')), 'Nothing') as TRANSPOSED_OUTPUT
RTRIM(REPLACE(REPLACE(REPLACE(VARCHAR(XMLSERIALIZE(XMLAGG(XMLELEMENT(NAME "A",ALIASNAME.ATTRIBUTENAME) ORDER BY ALIASNAME.ATTRIBUTENAME) AS CLOB)), '',','),'',''),'','')) AS TRANSPOSED_OUTPUT
Make sure you are casting your "ATTRIBUTENAME" to varchar in a subquery and then calling it here.
other possibility, with recursive cte
with tablewithrank as (
select id, category, rownumber() over(partition by category order by id) as rangid , (select count(*) from myTable f2 where f1.category=f2.category) nbidbycategory
from myTable f1
),
cte (id, category, rangid, nbidbycategory, rangconcat) as (
select id, category, rangid, nbidbycategory, cast(id as varchar(500)) from tablewithrank where rangid=1
union all
select f2.id, f2.category, f2.rangid, f2.nbidbycategory, cast(f1.rangconcat as varchar(500)) || ',' || cast(f2.id as varchar(500)) from cte f1 inner join tablewithrank f2 on f1.rangid=f2.rangid -1 and f1.category=f2.category
)
select category, rangconcat as IDS from cte
where rangid=nbidbycategory
Try this:
SELECT GROUP_CONCAT( field1, field2, field3 ,field4 SEPARATOR ', ')

DB2 Comma Separated Output by Groups

Is there a built in function for comma separated column values in DB2 SQL?
Example: If there are columns with an ID and it has 3 rows with the same ID but have three different roles, the data should be concatenated with a comma.
ID | Role
------------
4555 | 2
4555 | 3
4555 | 4
The output should look like the following, per row:
4555 2,3,4
LISTAGG function is new function in DB2 LUW 9.7
see example:
create table myTable (id int, category int);
insert into myTable values (1, 1);
insert into myTable values (2, 2);
insert into myTable values (5, 1);
insert into myTable values (3, 1);
insert into myTable values (4, 2);
example: select without any order in grouped column
select category, LISTAGG(id, ', ') as ids from myTable group by category;
result:
CATEGORY IDS
--------- -----
1 1, 5, 3
2 2, 4
example: select with order by clause in grouped column
select
category,
LISTAGG(id, ', ') WITHIN GROUP(ORDER BY id ASC) as ids
from myTable
group by category;
result:
CATEGORY IDS
--------- -----
1 1, 3, 5
2 2, 4
I think with this smaller query, you can do what you want.
This is equivalent of MySQL's GROUP_CONCAT in DB2.
SELECT
NUM,
SUBSTR(xmlserialize(xmlagg(xmltext(CONCAT( ', ',ROLES))) as VARCHAR(1024)), 3) as ROLES
FROM mytable
GROUP BY NUM;
This will output something like:
NUM ROLES
---- -------------
1 111, 333, 555
2 222, 444
assumming your original result was something like that:
NUM ROLES
---- ---------
1 111
2 222
1 333
2 444
1 555
Depending of the DB2 version you have, you can use XML functions to achieve this.
Example table with some data
create table myTable (id int, category int);
insert into myTable values (1, 1);
insert into myTable values (2, 2);
insert into myTable values (3, 1);
insert into myTable values (4, 2);
insert into myTable values (5, 1);
Aggregate results using xml functions
select category,
xmlserialize(XMLAGG(XMLELEMENT(NAME "x", id) ) as varchar(1000)) as ids
from myTable
group by category;
results:
CATEGORY IDS
-------- ------------------------
1 <x>1</x><x>3</x><x>5</x>
2 <x>2</x><x>4</x>
Use replace to make the result look better
select category,
replace(
replace(
replace(
xmlserialize(XMLAGG(XMLELEMENT(NAME "x", id) ) as varchar(1000))
, '</x><x>', ',')
, '<x>', '')
, '</x>', '') as ids
from myTable
group by category;
Cleaned result
CATEGORY IDS
-------- -----
1 1,3,5
2 2,4
Just saw a better solution using XMLTEXT instead of XMLELEMENT here.
Since DB2 9.7.5 there is a function for that:
LISTAGG(colname, separator)
check this for more information: Using LISTAGG to Turn Rows of Data into a Comma Separated List
My problem was to transpose row fields(CLOB) to column(VARCHAR) with a CSV and use the transposed table for reporting. Because transposing on report layer slows down the report.
One way to go is to use recursive SQL. You can find many articles about that but its difficult and resource consuming if you want to join all your recursive transposed columns.
I created multiple global temp tables where I stored single transposed columns with one key identifier. Eventually, I had 6 temp tables for joining 6 columns but due to limited resource allocation I wasnt able to bring all columns together. I opted to below 3 formulas and then I just had to run 1 query which gave me output in 10 seconds.
I found various articles on using XML2CLOB functions and have found 3 different ways.
REPLACE(VARCHAR(XML2CLOB(XMLAGG(XMLELEMENT(NAME "A",ALIASNAME.ATTRIBUTENAME)))),'', ',') AS TRANSPOSED_OUTPUT
NVL(TRIM(',' FROM REPLACE(REPLACE(REPLACE(CAST(XML2CLOB(XMLAGG(XMLELEMENT(NAME "E", ALIASNAME.ATTRIBUTENAME))) AS VARCHAR(100)),'',' '),'',','), '', 'Nothing')), 'Nothing') as TRANSPOSED_OUTPUT
RTRIM(REPLACE(REPLACE(REPLACE(VARCHAR(XMLSERIALIZE(XMLAGG(XMLELEMENT(NAME "A",ALIASNAME.ATTRIBUTENAME) ORDER BY ALIASNAME.ATTRIBUTENAME) AS CLOB)), '',','),'',''),'','')) AS TRANSPOSED_OUTPUT
Make sure you are casting your "ATTRIBUTENAME" to varchar in a subquery and then calling it here.
other possibility, with recursive cte
with tablewithrank as (
select id, category, rownumber() over(partition by category order by id) as rangid , (select count(*) from myTable f2 where f1.category=f2.category) nbidbycategory
from myTable f1
),
cte (id, category, rangid, nbidbycategory, rangconcat) as (
select id, category, rangid, nbidbycategory, cast(id as varchar(500)) from tablewithrank where rangid=1
union all
select f2.id, f2.category, f2.rangid, f2.nbidbycategory, cast(f1.rangconcat as varchar(500)) || ',' || cast(f2.id as varchar(500)) from cte f1 inner join tablewithrank f2 on f1.rangid=f2.rangid -1 and f1.category=f2.category
)
select category, rangconcat as IDS from cte
where rangid=nbidbycategory
Try this:
SELECT GROUP_CONCAT( field1, field2, field3 ,field4 SEPARATOR ', ')