I want row data of same id in one single row - sql

The Table Format is like
ID | MID | PID | Quantity
1 | 1 | 2 | 3
2 | 1 | 3 | 10
3 | 2 | 2 | 11
4 | 2 | 1 | 5
I want to result as following
ID | MID | Final
1 | 1 | 2(3),3(10)
2 | 2 | 2(11),1(5)

first concate two columns and then do string_agg. Here is the demo.
with cte as
(
select
mid,
concat(pid, '(', quantity, ')') as concat_col
from table1
)
select
row_number() over (order by mid) as id,
mid,
string_agg(concat_col, ', ') as final
from cte
group by
mid
output:
| id | mid | final |
| --- | --- | ----------- |
| 1 | 1 | 2(3), 3(10) |
| 2 | 2 | 2(11), 1(5) |
If you are using older version of SQL Server then try the following
with cte as
(
select
mid,
concat(pid, '(', quantity, ')') as concat_col
from table1
)
select
row_number() over (order by mid) as id,
mid,
stuff((
select ',' + concat_col
from cte c1
where c.mid = c1.mid
for XML PATH('')
), 1, 1, '') as final
from cte c
group by
mid

select MID, string_agg(concat(PID, '(', Quantity,')'), ', ')
from dbo.Sample
group by MID
Result :
MID FINAL
1 2(3), 3(10)
2 2(11), 1(5)

Related

Generate three rows in select query

I have following table
SELECT TableCode, Col1, Col2
FROM TableA
WHERE TableCode = 23
Result of Table:
TableCode | Col1 | Col1
23 | CustCode | QS
23 | CatCode | QS
After that i wrote one query on TableA which return following output
Query :
SELECT TableCode,x.ColCode,
x.ColumnName + '_' + CONVERT(VARCHAR(5), ROW_NUMBER() OVER (PARTITION BY X.COL ORDER BY X.COL)) [ColumnName],X.Values,
ROW_NUMBER() OVER (PARTITION BY X.COL ORDER BY X.COL) [RowNo]
FROM TableA a CROSS APPLY
(SELECT 1 ColCode,'ParaName' ColumnName,Col1 Values
UNION ALL
SELECT 2,'ParaSource',Col2
) x
WHERE TableCode = 23;
Result :
TableCode | ColCode | ColumnName | Values | RowNo
23 | 1 | ParaName_1 | CustCode | 1
23 | 1 | ParaName_2 | CatCode | 2
23 | 2 | ParaSource_1 | QS | 1
23 | 2 | ParaSource_2 | QS | 2
And i required following output:
Required Output :
TableCode | ColCode | ColumnName | Values | RowNo
23 | 1 | ParaName_1 | CustCode | 1
23 | 1 | ParaName_2 | CatCode | 2
23 | 1 | ParaName_3 | Null | 3
23 | 2 | ParaSource_1 | QS | 1
23 | 2 | ParaSource_2 | QS | 2
23 | 2 | ParaSource_3 | null | 3
Using a couple of common table expressions and row_number() along with the table value constructor (values (...),(...))
to cross join numbers 1, 2, and 3 then using a left join to return 3 rows per TableCode even when you do not have three rows in the source table.
;with numbered as (
select *, rn = row_number() over (order by (select 1))
from TableA
where TableCode = 23
)
, cte as (
select distinct tc.TableCode, a.Col1, a.Col2, v.rn
from numbered tc
cross join (values (1),(2),(3)) v (rn)
left join numbered a
on a.TableCode = tc.TableCode
and a.rn = v.rn
)
select
a.TableCode
, x.ColCode
, [ColumnName] = x.ColumnName + '_' + convert(varchar(5),a.rn)
, X.Value
,[RowNo] = a.rn
from cte a
cross apply (values (1,'ParaName',Col1),(2,'ParaSource',Col2))
as x(ColCode, ColumnName, Value)
order by ColCode, RowNo;
rextester demo: http://rextester.com/CJU8986
returns:
+-----------+---------+--------------+----------+-------+
| TableCode | ColCode | ColumnName | Value | RowNo |
+-----------+---------+--------------+----------+-------+
| 23 | 1 | ParaName_1 | CustCode | 1 |
| 23 | 1 | ParaName_2 | CatCode | 2 |
| 23 | 1 | ParaName_3 | NULL | 3 |
| 23 | 2 | ParaSource_1 | QS | 1 |
| 23 | 2 | ParaSource_2 | QS | 2 |
| 23 | 2 | ParaSource_3 | NULL | 3 |
+-----------+---------+--------------+----------+-------+
This would appear to do what you want:
SELECT TableCode, x.ColCode, v.*
FROM TableA a CROSS APPLY
(VALUES (1, 'ParaName-1', Col1, 1),
(2, 'ParaName-2', Col2, 2),
(3, 'ParaName-3', NULL, 2)
) v(ColCode, ColumnName, [Values], RowNo)
WHERE TableCode = 23;
I see no reason to use row_number() when you can just read in the correct values. Also, VALUES is a SQL keyword so it is a really bad column name.

How to split each row into multiple rows

My table has three rows:
ID MULTI_CODES
1 10-101-102
2 20-201-202
3 30-301-302
How to write a statement to split each row like this considering delimited code
ID SINGLE_CODE LEVEL
1 10 1
1 101 2
1 102 3
2 20 1
2 201 2
2 202 3
3 30 1
3 301 2
3 302 3
If you are on SQL Server 2016 you can use the built in string_split function.
If not you will need your own function. One that performs very well is Jeff Moden's tally table method. My altered version of this looks like this, allowing for user specified delimiters and if required specific values:
create function dbo.StringSplit
(
#str nvarchar(4000) = ' ' -- String to split.
,#delimiter as nvarchar(1) = ',' -- Delimiting value to split on.
,#num as int = null -- Which value to return.
)
returns table
as
return
(
-- Start tally table with 10 rows.
with n(n) as (select n from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n(n))
-- Select the same number of rows as characters in isnull(#str,'') as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest isnull(#str,'') length.
,t(t) as (select top (select len(isnull(#str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(isnull(#str,''),t,1) = #delimiter)
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(#delimiter,isnull(#str,''),s),0)-s,4000) from s)
select rn as ItemNumber
,Item
from(select row_number() over(order by s) as rn
,substring(isnull(#str,''),s,l) as item
from l
) a
where rn = #num -- Return a specific value where specified,
or #num is null -- Or everything where not.
)
go
And is used as follows:
declare #t table (ID int, MULTI_CODES nvarchar(50));
insert into #t values (1,'10-101-102'),(2,'20-201-202'),(3,'30-301-302');
select t.ID
,t.MULTI_CODES
,s.Item as SINGLE_CODE
,s.ItemNumber as [Level]
from #t t
outer apply dbo.StringSplit(t.MULTI_CODES,'-',null) s
order by t.ID
,s.ItemNumber;
Which outputs:
+----+-------------+-------------+-------+
| ID | MULTI_CODES | SINGLE_CODE | Level |
+----+-------------+-------------+-------+
| 1 | 10-101-102 | 10 | 1 |
| 1 | 10-101-102 | 101 | 2 |
| 1 | 10-101-102 | 102 | 3 |
| 2 | 20-201-202 | 20 | 1 |
| 2 | 20-201-202 | 201 | 2 |
| 2 | 20-201-202 | 202 | 3 |
| 3 | 30-301-302 | 30 | 1 |
| 3 | 30-301-302 | 301 | 2 |
| 3 | 30-301-302 | 302 | 3 |
+----+-------------+-------------+-------+
IF OBJECT_ID('tempdb..#TEMPtable') IS NOT NULL
Drop table #TEMPtable
;With cte(ID, MULTI_CODES)
AS
(
select 1,'10-101-102' UNION ALL
select 2,'20-201-202' UNION ALL
select 3,'30-301-302'
)
SELECT * INTO #TEMPtable FROM cte
SELECT ID, Split.a.value('.','Varchar(100)') AS MULTI_CODES,ROW_NUMBER()Over(Partition by ID Order by ID) AS LEVEL
FROM(
SELECT ID, CASt('<M>' + Replace(MULTI_CODES,'-','</M><M>') +'</M>' AS XML)As MULTI_CODES
FROM #TEMPtable
)AS A
CROSS APPLY
MULTI_CODES.nodes('/M') AS Split(A)
OutPut
ID MULTI_CODES LEVEL
----------------------
1 10 1
1 101 2
1 102 3
2 20 1
2 201 2
2 202 3
3 30 1
3 301 2
3 302 3
you can use cross apply to string_split function
select id, Value as Single_code, RowN as [Level] from #yourcodes cross apply
(
select RowN= row_number() over (order by (select null)), value
from string_split(Multi_codes, '-')
) a
Output:
+----+-------------+-------+
| id | Single_code | Level |
+----+-------------+-------+
| 1 | 10 | 1 |
| 1 | 101 | 2 |
| 1 | 102 | 3 |
| 2 | 20 | 1 |
| 2 | 201 | 2 |
| 2 | 202 | 3 |
| 3 | 30 | 1 |
| 3 | 301 | 2 |
| 3 | 302 | 3 |
+----+-------------+-------+

SQL Sum and Group by changing value of group

How do I group values ​​and change one of them when there is more than one
Table
ID | VALUE | NAME
1 | 2 | John
1 | 5 | Carl
2 | 4 | Elis
2 | 1 | Ted
3 | 2 | James
RESULT
ID | VALUE | NAME
1 | 7 | *
2 | 5 | *
3 | 2 | James
Here is one way that should work in any database:
select id, sum(value) as value,
(case when min(name) = max(name) then min(name) else '*' end) as name
from t
group by id;
This is the others query you must try
Select ID, sum(Value) Value, NAME = ( Select b.Name + ' ' AS [text()]
From dbo.test b
Where a.ID = b.ID
ORDER BY a.ID
For XML PATH (''))
from test a
group by ID
order by ID

string aggregate group and count on a value

I have table like this.
| table |
| class_id| name | gender |
+---------+---------+----------+
| 1 | Jane | F |
| 1 | John | M |
| 1 | Tom | M |
| 1 | Bob | M |
| 2 | Jack | M |
| 2 | Kate | F |
I have a query like this.
select id, array_to_string(array_agg(name), ' - '::text) as name_list from table
group by class_id
My result is
| 1 | Jane-John-Tom-Bob |
But i'd like to count my gender count also i mean in the first group (cass 1) i need a column like 1 F + 3 M
My request is something like this and i'd like to use it in 1 group by.
| 1 | Jane-John-Tom-Bob |1F + 3M
You can do that with a filtered aggregate:
select id,
string_agg(name, ' - ') as name_list,
concat(
count(*) filter (where gender = 'F'),
'F + ',
count(*) filter (where gender = 'M'),
'M') as gender_count
from table
group by class_id;
If you are on an older Postgres version, you need to replace
count(*) filter (where gender = 'F')
with
count(case when gender = 'F' then 1 end)
(and the same for 'M')
There is also another solution without using Filter aggregate
select tt.class_id, string_agg ( t, ','::text) as gender, string_agg(distinct y,','::text) as name
from
(
select class_id, count(gender)::text|| string_agg( distinct gender, ',' ) as t
from string_test
group by class_id , gender
) tt ,
(
select class_id, string_agg( distinct name::text, ','::text ) as y
from string_test
group by class_id
) yy
where tt.class_id=yy.class_id
group by tt.class_id
Result;
+==========+========+===================+
| class_id | gender | name |
+==========+========+===================+
| 1 | 1F,3M | Bob,Jane,John,Tom |
+----------+--------+-------------------+
| 2 | 1F,1M | Jack,Kate |
+==========+========+===================+

2 listagg in one SQL Select in Oracle

I have a table in the form of :
| ID | COURSE | PASS |
---------------------------
| 1 | 1 | 1 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 1 | 4 | 0 |
| 1 | 5 | 0 |
and I want row in the form:
| ID | FAILED | PASSED |
---------------------------
| 1 | 4,5 | 1,2,3 |
the only i figured is something like this:
select NVL(passed.id, failed.id), passed.test, failed.test from
(select id, listagg(course, ',') within group (order by course) test from table1 where pass = 1 group by id ) passed
full outer join
(select id, listagg(course, ',') within group (order by course) test from table1 where pass = 0 group by id ) failed
on passed.id = failed.id
is there a way to do it in a single query ?
Try
select id,
listagg(case when pass = 1 then course end, ',') within group (order by course) passed,
listagg(case when pass = 0 then course end, ',') within group (order by course) failed
from table1
group by id
Here is a sqlfiddle demo