Lets say i have the following table:
id col1 col2 col3
-- ---- ---- -----
1 A A A
2 B B B
3 C C C
I want an sql query to create a resultset concatenating all the columns into one comma separated string but without specify the columns (col1, col2, col3) in the selection:
'A', 'A', 'A'
'B', 'B', 'B'
'C', 'C', 'C'
So far i have tried the following but this brings me just one row with all the rows in one string:
select concat('''', string_agg(value, ''','''), '''') as res
from (
select (json_each_text(row_to_json(test_table))).value
from test_table
) x
Result: 'A', 'A', 'A','B', 'B', 'B','C', 'C', 'C'
Assuming the id is unique, you should group the rows by it, e.g.
select id, string_agg(quote_literal(value), ',')
from test_table t
cross join jsonb_each_text(to_jsonb(t)- 'id')
group by id
order by id
Test it in Db<>Fiddle.
You need to group by a unique column in order to get one row per original row:
select id, string_agg(val, ',')
from (
select id, r.val
from test_table t
cross join jsonb_each_text(to_jsonb(t) - 'id') as r(key, val)
) x
group by id
order by id;
If a JSONB array of all column values is an alternative you can live with, this is a bit simpler:
select id,
jsonb_path_query_array(to_jsonb(t) - 'id', '$.keyvalue().value')
from test_table
order by id;
Related
I'm trying to join a sub query but as the table contains no unique row_id and I need the most recent record of a specific type, I want to do an Order by Date Desc and get the Top 1 from that, but it doesn't seem allowed in Sybase ASE.
I put together a small sample to show kinda what i'm trying to do.
CREATE TABLE #test_users (USER_ID CHAR(9), USER_CK INT)
INSERT INTO #test_users (USER_ID, USER_CK)
SELECT 'QA0000001', 123000010
UNION ALL SELECT 'QA0000002', 123000020
UNION ALL SELECT 'QA0000003', 123000030
UNION ALL SELECT 'QA0000004', 123000040
UNION ALL SELECT 'QA0000005', 123000050
CREATE TABLE #test_records (STAT_TYPE CHAR(3), USER_CK INT, MOD_DT DATE, PLAN_ID CHAR(1))
INSERT INTO #test_records (STAT_TYPE, USER_CK, MOD_DT, PLAN_ID)
SELECT 'ADD', 123000010, '8/1/2017', 'A'
UNION ALL SELECT 'TRM', 123000010, '6/1/2018', 'A'
UNION ALL SELECT 'ADD', 123000010, '6/1/2018', 'B'
UNION ALL SELECT 'ADD', 123000020, '5/1/2017', 'A'
UNION ALL SELECT 'TRM', 123000020, '9/1/2018', 'A'
UNION ALL SELECT 'ADD', 123000030, '3/1/2018', 'A'
UNION ALL SELECT 'ADD', 123000040, '4/1/2018', 'A'
UNION ALL SELECT 'ADD', 123000050, '1/1/2018', 'B'
UNION ALL SELECT 'TRM', 123000050, '7/1/2018', 'B'
UNION ALL SELECT 'ADD', 123000050, '7/1/2018', 'A'
--Want to know everyone that has ever been in Plan A, and what their last status type was.
--Should return
-- QA0000001, A, TRM, 6/1/2018
-- QA0000002, A, TRM, 9/1/2018
-- QA0000003, A, ADD, 3/1/2018
-- QA0000004, A, ADD, 4/1/2018
-- QA0000005, A, ADD, 7/1/2018
SELECT u.USER_ID, r.PLAN_ID, r.STAT_TYPE, r.MOD_DT FROM #test_users u
LEFT JOIN #test_records r
ON PLAN_ID = 'A'
AND u.USER_CK = r.USER_CK
Thanks for any help you could provide.
Mike
I've a table like this:
Source table "tab"
column1 column2
x 1
x 2
y 1
y 2
y 3
z 3
How can I build the query to get result with unique values in each of two columns separately. For example I'd like to get a result like one of these sets:
column1 column2
x 1
y 2
z 3
or
column1 column2
x 2
y 1
z 3
or ...
Thanks.
What you're asking for is difficult because it's weird: SQL treats rows as related fields but you're asking to make two separate lists (distinct values from col1 and distinct values from col2) then display them in one output table not caring how the rows match up.
You can so this by writing the SQL along those lines. Write a separate select distinct for each column, then put them together somehow. I'd put them together by giving each row in each results a row number, then joining them both to a big list of numbers.
It's not clear what you want null to mean. Does it mean there's a null in one of the columns, or that there's not the same number of distinct values in each column? This one problem from asking for things that don't match up with typical relational logic.
Here's an example, removing the null value from the data since that confuses the issue, different data values to avoid confusing rowNumber with data and so there are 3 distinct values in one column and 4 in another. This works for SQL Server, presumably there's a variation for PostgreSQL.
if object_id('mytable') is not null drop table mytable;
create table mytable ( col1 nvarchar(10) null, col2 nvarchar(10) null)
insert into mytable
select 'x', 'a'
union all select 'x', 'b'
union all select 'y', 'c'
union all select 'y', 'b'
union all select 'y', 'd'
union all select 'z', 'a'
select c1.col1, c2.col2
from
-- derived table giving distinct values of col1 and a rownumber column
( select col1
, row_number() over (order by col1) as rowNumber
from ( select distinct col1 from mytable ) x ) as c1
full outer join
-- derived table giving distinct values of col2 and a rownumber column
( select col2
, row_number() over (order by col2) as rowNumber
from ( select distinct col2 from mytable ) x ) as c2
on c1.rowNumber = c2.rowNumber
I have a table with an XML type column. This column contains a dynamic list of attributes that may be different between records.
I am trying to GROUP BY COUNT over these attributes without having to go through the table separately for each attribute.
For example, one record could have attributes A, B and C and the other would have B, C, D then, when I do the GROUP BY COUNT I would get A = 1, B = 2, C = 2 and D = 1.
Is there any straightforward way to do this?
EDIT in reply to Andrew's answer:
Because my knowledge of this construct is superficial at best I had to fiddle with it to get it to do what I want. In my actual code I needed to group by the TimeRange, as well as only select some attributes depending on their name. I am pasting the actual query below:
WITH attributes AS (
SELECT
Timestamp,
N.a.value('#name[1]', 'nvarchar(max)') AS AttributeName,
N.a.value('(.)[1]', 'nvarchar(max)') AS AttributeValue
FROM MyTable
CROSS APPLY AttributesXml.nodes('/Attributes/Attribute') AS N(a)
)
SELECT Datepart(dy, Timestamp), AttributeValue, COUNT(AttributeValue)
FROM attributes
WHERE AttributeName IN ('AttributeA', 'AttributeB')
GROUP BY Datepart(dy, Timestamp), AttributeValue
As a side-note: Is there any way to reduce this further?
WITH attributes AS (
SELECT a.value('(.)[1]', 'nvarchar(max)') AS attribute
FROM YourTable
CROSS APPLY YourXMLColumn.nodes('//path/to/attributes') AS N(a)
)
SELECT attribute, COUNT(attribute)
FROM attributes
GROUP BY attribute
CROSS APPLY is like being able to JOIN the xml as a table. The WITH is needed because you can't have xml methods in a group clause.
Here is a way to get the attribute data into a way that you can easily work with it and reduce the number of times you need to go through the main table.
--create test data
declare #tmp table (
field1 varchar(20),
field2 varchar(20),
field3 varchar(20))
insert into #tmp (field1, field2, field3)
values ('A', 'B', 'C'),
('B', 'C', 'D')
--convert the individual fields from seperate columns to one column
declare #table table(
field varchar(20))
insert into #table (field)
select field1 from #tmp
union all
select field2 from #tmp
union all
select field3 from #tmp
--run the group by and get the count
select field, count(*)
from #table
group by field
Say I have a table A and it has 5 columns (Column1, Column2.. Column5), the values in each column is one char size and stored only
as alphabetic as follows
ID Column1 Column2 Column3 Column4 Column5
1 A C D A B
2 A D A B A
3 B K Q C Q
4 A K E E B
5 F K F F S
I need a count of each different value stored in column1 to column5, I want the following information
Column1 has A's count=3, B's count=1, F's count=1
Column2 has C's count=1, D's count=1, K's count=3
and so on
What is the correct way and format to return these values?
Thanks
You could try:
SELECT 'Col1' As 'Col', Column1 as 'Value', COUNT(*) as 'Ct'
FROM MyTable
GROUP BY Column1
UNION ALL
SELECT 'Col2', Column2, COUNT(*)
FROM MyTable
GROUP BY Column2
...
You will need to write an additional SELECT to UNION for each column you want to aggregate, but it will return the data you are after.
Can you just execute an individual query for each needed column using a GROUP BY?
SELECT Column1, COUNT(Column1) FROM TableName
GROUP BY Column1
You can use unpivot in a derived table (or CTE) and group by column name and value in the outer query.
Try this:
declare #T table
(
ID int,
Column1 char(1),
Column2 char(1),
Column3 char(1),
Column4 char(1),
Column5 char(1)
)
insert into #T values
(1, 'A', 'C', 'D', 'A', 'B'),
(2, 'A', 'D', 'A', 'B', 'A'),
(3, 'B', 'K', 'Q', 'C', 'Q'),
(4, 'A', 'K', 'E', 'E', 'B'),
(5, 'F', 'K', 'F', 'F', 'S')
;with C as
(
select ID, Col, Val
from (
select ID, Column1, Column2, Column3, Column4, Column5
from #T
) as T
unpivot (Val for Col in (Column1, Column2, Column3, Column4, Column5)) as U
)
select Col, Val, count(*) as ValCount
from C
group by Col, Val
order by Col
The union approach is going to server you best. The individual query for each union would look something like this:
Select Distinct Column1, COUNT(Column1)OVER(PARTITION BY Column1) Col1Count, 'Column1' ColumnName
From ColTable
Union All
...
I'm trying to condense a mappings table into a concatenated XML string. Essentially, I want to take this table:
Old_Key Old_Value
1 'a'
1 'b'
1 'c'
2 'd'
2 'e'
And insert it so that the values in col2 are turned into an XML string for each value in Col1, like so:
New_Key New_Value
1 <vals><val>a</val><val>b</val><val>c</val><vals>
2 <vals><val>d</val><val>e</val></vals>
My current concatenation code is:
INSERT INTO New_Table (New_Key, New_Value)
SELECT DISTINCT(Old_Key), (SELECT Old_Value AS val FROM Old_Table FOR XML PATH(''), ROOT('vals')) FROM Old_Table
This code doesn't work, since all of the Old_Values are being concatenated together. How can I make sure that only Old_Values that share the same key are concatenated together? Let me know if there's anything else I can do to clarify my situation. Thanks!
Join against the outer Old_Table in the sub query and use group by instead of distinct.
select
O1.Old_Key,
(select Old_Value as val
from Old_Table as O2
where O1.Old_Key = O2.Old_Key
for xml path(''), root('vals'), type) as Keys
from Old_Table as O1
group by O1.Old_Key
Result
Old_Key Keys
------- -------------------------------------------------
1 <vals><val>a</val><val>b</val><val>c</val></vals>
2 <vals><val>d</val><val>e</val></vals>