How do I combine data from multiple rows into one? - sql

I’m using SQL Server 2008. I have data as in this table:
Team Email Groups
------- ------------------ ------
|Team1|-|email0#email.com|-|A|
|Team1|-|email1#email.com|-|B|
|Team1|-|email2#email.com|-|C|
|Team2|-|email3#email.com|-|A|
|Team2|-|email4#email.com|-|B|
|Team2|-|email5#email.com|-|C|
I want to get the data in this format:
Team A B C
------- ------------------ ------------------ ------------------
|Team1|-|email0#email.com|-|email1#email.com|-|email2#email.com|
|Team2|-|email3#email.com|-|email4#email.com|-|email5#email.com|
How can I achieve this?

Using PIVOT You can do the following
With SampleData AS
(
SELECT 'Team1' as Team , 'email0#email.com' as email, 'A' as Groups
UNION SELECT 'Team1' as Team , 'email1#email.com' as email, 'B' as Groups
UNION SELECT 'Team1' as Team , 'email2#email.com' as email, 'C' as Groups
UNION SELECT 'Team2' as Team , 'email3#email.com' as email, 'A' as Groups
UNION SELECT 'Team2' as Team , 'email4#email.com' as email, 'B' as Groups
UNION SELECT 'Team2' as Team , 'email5#email.com' as email, 'C' as Groups
)
SELECT Team, A, B,C FROM
(SELECT * FROM SampleData) source
PIVOT
(MAX(email) FOR Groups IN ([A], [B], [C]) )as pvt
Produces
Team A B C
----- ---------------- ---------------- ----------------
Team1 email0#email.com email1#email.com email2#email.com
Team2 email3#email.com email4#email.com email5#email.com
See a working Data.SE example
In a DB that doesn't support PIVOT you can instead do multiple joins to your table. Although you may want to anyway, since as GBN pointed out, since we're not using an aggregate.
With SampleData AS
(
SELECT 'Team1' as Team , 'email0#email.com' as email, 'A' as Groups
UNION SELECT 'Team1' as Team , 'email1#email.com' as email, 'B' as Groups
UNION SELECT 'Team1' as Team , 'email2#email.com' as email, 'C' as Groups
UNION SELECT 'Team2' as Team , 'email3#email.com' as email, 'A' as Groups
UNION SELECT 'Team2' as Team , 'email4#email.com' as email, 'B' as Groups
UNION SELECT 'Team2' as Team , 'email5#email.com' as email, 'C' as Groups
)
SELECT
source.Team,
A.email,
B.email,
C.email
FROM
(SELECT DISTINCT TEAM From SampleData) source
LEFT JOIN SampleData A
ON source.Team = A.Team
AND A.GROUPS = 'A'
LEFT JOIN SampleData B
ON source.Team = B.Team
AND B.GROUPS = 'B'
LEFT JOIN SampleData C
ON source.Team = C.Team
AND C.GROUPS = 'C'
See a working Data.SE example

Related

SQL occurrences of combinations into a matrix

This seems like a rather common thing to do/query, but I'm not sure what is the best way to approach this and I cannot find similar examples. Basically I have 5 different systems where I've extracted the unique user IDs from the user log table. I want to know the overlap of users across the systems. The resulting tables are like this (user IDs are shared between systems):
sysA
-----
user1
user2
user3
sysB
-----
user2
user3
user4
user5
sysC
-----
user5
Now the output should be like this:
sysA sysB sysC sysD sysE count_distinct(userkey)
1 0 0 0 0 1
1 1 0 0 0 2
1 0 1 0 0 0
etc.
I tried doing this by using GROUP BY CUBE, which is something specific to Oracle and seemed useful in this case, but I couldn't get it to work as it seems I need to join every possible combination first in order to get the right result. Another thing I tried is this:
SELECT sysA_flag, sysB_flag, sysC_flag, sysD_flag, sysE_flag, COUNT(*)
FROM (
SELECT DISTINCT userId, 1 sysA_flag
FROM sysA_input_table
) sysA
FULL OUTER JOIN (
SELECT DISTINCT userId, 1 sysB_flag
FROM sysB_input_table
) sysB
ON sysA.userId = sysB.userId
FULL OUTER JOIN (
SELECT DISTINCT userId, 1 sysC_flag
FROM sysC_input_table
) sysC
ON sysA.userId = sysC.userId
FULL OUTER JOIN (
SELECT DISTINCT userId, 1 sysD_flag
FROM sysD_input_table
) sysD
ON sysA.userId = sysD.userId
FULL OUTER JOIN (
SELECT DISTINCT userId, 1 sysE_flag
FROM sysE_input_table
) sysE
ON sysA.userId = sysE.userId
GROUP BY (sysA_flag, sysB_flag, sysC_flag, sysD_flag, sysE_flag)
In principle this gives the right output, but doesn't give all combinations (only for sysA). It might work by expanding on this, but it seems like an inefficient way to do it.
How should this be done in a proper way?
The straightforward way should be something like this to start with. Answering the "how to do it" part of your question.
with sysA as (
select 'user1' as user_id from dual union all
select 'user2' from dual union all
select 'user3' from dual
),
sysB as
(
select 'user2' from dual union all
select 'user3' from dual union all
select 'user4' from dual union all
select 'user5' from dual
),
sysC as
(
select 'user5' from dual
), cte as (
select * from (
select t1.*,'a' sys from sysa t1
union all
select t1.*,'b' from sysb t1
union all
select t1.*,'c' from sysc t1
) x
pivot (count(*) for sys in ('a' as a,'b' as b,'c' as c))
),
combinations as (select * from (select level-1 as a from dual connect by level <= 2) t1
cross join (select level-1 as b from dual connect by level <= 2) t2
cross join (select level-1 as c from dual connect by level <= 2) t3
)
select t1.a,t1.b,t1.c,count(t2.user_id) as user_count
from combinations t1
left join cte t2 on t1.a = t2.a and t1.b = t2.b and t1.c = t2.c
group by t1.a,t1.b,t1.c
--having t1.a + t1.b + t1.c = 2
fiddle

Oracle SQL - Count based on a condition to include distinct rows with zero matches

Is there a "better" way to refactor the query below that returns the number occurrences of a particular value (e.g. 'A') for each distinct id? The challenge seems to be keeping id = 2 in the result set even though the count is zero (id = 2 is never related to 'A'). It has a common table expression, NVL function, in-line view, distinct, and left join. Is all of that really needed to get this job done? (Oracle 19c)
create table T (id, val) as
select 1, 'A' from dual
union all select 1, 'B' from dual
union all select 1, 'A' from dual
union all select 2, 'B' from dual
union all select 2, 'B' from dual
union all select 3, 'A' from dual
;
with C as (select id, val, count(*) cnt from T where val = 'A' group by id, val)
select D.id, nvl(C.cnt, 0) cnt_with_zero from (select distinct id from T) D left join C on D.id = C.id
order by id
;
ID CNT_WITH_ZERO
---------- -------------
1 2
2 0
3 1
A simple way is conditional aggregation:
select id,
sum(case when val = 'A' then 1 else 0 end) as num_As
from t
group by id;
If you have another table with one row per id, you I would recommend:
select i.id,
(select count(*) from t where t.id = i.id and t.val = 'A') as num_As
from ids i;

COUNT & PRINT matched Row values in NEW COLUMNS

I am trying to make the column name from row value and count the total value of each row name to show into the particular column name. Result will show from 30 days.
SELECT id
, Name
, Designation
, DeptName
, Sts
, COUNT(Sts) as 'COUNT'
FROM table
WHERE Date >= DATEADD(month,-1,GETDATE())
AND Name = 'Neo'
GROUP BY id, Name, Designation, DeptName, Sts
ORDER id
Here is my output
Required Output is...
use conditional aggregation
SELECT id
, Name
, Designation
, DeptName
, sum (case when Sts='H' then 1 end) H
,sum (case when Sts='A' then 1 end) A
,sum (case when Sts='P' then 1 end) P
FROM table
WHERE Date >= DATEADD(month,-1,GETDATE())
AND Name = 'Neo'
GROUP BY id, Name, Designation, DeptName
ORDER id
The CASE statement goes through conditions and return a value when the first condition is met (like an IF-THEN-ELSE statement) and when a condition will meet it will sum up for that particular value
As eariler suggested, use pivot
WITH temp AS(
SELECT 1 AS id,'Neo' AS name,'Zonal Manager' AS designation, 'Admin' AS deptname, 'H' AS sts ,2 AS cnt FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',3 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'P',4 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',1 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',8 FROM dual )
SELECT p.*
FROM temp
PIVOT(
SUM(cnt)
FOR sts
IN ('A', 'P', 'H')
) p

How to delete/update nested data in bigquery

Is there a way to delete/update nested field in bigquery?
Let's say I have this data
wives.age wives.name name
21 angel adam
20 kale
21 victoria rossi
20 jessica
or in json:
{"name":"adam","wives":[{"name":"angel","age":21},{"name":"kale","age":20}]}
{"name":"rossi","wives":[{"name":"victoria","age":21},{"name":"jessica","age":20}]}
As you can see from the data above.
Adam has 2 wives, named angel and kale. How to:
Delete kale record.
Update jessica to dessica
I tried to google this, but can't find it. I also tried to unnest, etc but no luck.
The reason why we want to do this is because we insert the array to the wrong records and want to remove/update array data with some condition.
Below is for BigQuery Standard SQL
#standardSQL
WITH updates AS (
SELECT 'rossi' name, 'jessica' oldname, 'dessica' newname UNION ALL
SELECT 'rossi' name, 'victoria' oldname, 'polly' newname UNION ALL
SELECT 'adam' name, 'angel' oldname, 'jen' newname
), divorces AS (
SELECT 'adam' name, 'kale' wifename UNION ALL
SELECT 'adam' name, 'milly' wifename UNION ALL
SELECT 'rossi' name, 'linda' wifename
)
SELECT t.name,
ARRAY(
SELECT AS STRUCT
age,
CASE
WHEN NOT oldname IS NULL THEN newname
ELSE name
END name
FROM UNNEST(wives)
LEFT JOIN UNNEST(updates) ON t.name = u.name AND name = oldname
LEFT JOIN UNNEST(divorces) AS wifename ON t.name = d.name AND name = wifename
WHERE wifename IS NULL
) waves
FROM `project.dataset.table` t
LEFT JOIN (
SELECT name, ARRAY_AGG(STRUCT(oldname, newname)) updates
FROM updates GROUP BY name
) u ON t.name = u.name
LEFT JOIN (
SELECT name, ARRAY_AGG(wifename) divorces
FROM divorces GROUP BY name
) d ON t.name = d.name
You can test / play with above using dummy data as below
#standardSQL
WITH `project.dataset.table` AS (
SELECT 'adam' name, [STRUCT<age INT64, name STRING>(21, 'angel'), (20, 'kale'), (22, 'milly')] wives UNION ALL
SELECT 'rossi', [STRUCT<age INT64, name STRING>(21, 'victoria'), (20, 'jessica'), (23, 'linda')]
), updates AS (
SELECT 'rossi' name, 'jessica' oldname, 'dessica' newname UNION ALL
SELECT 'rossi' name, 'victoria' oldname, 'polly' newname UNION ALL
SELECT 'adam' name, 'angel' oldname, 'jen' newname
), divorces AS (
SELECT 'adam' name, 'kale' wifename UNION ALL
SELECT 'adam' name, 'milly' wifename UNION ALL
SELECT 'rossi' name, 'linda' wifename
)
SELECT t.name,
ARRAY(
SELECT AS STRUCT
age,
CASE
WHEN NOT oldname IS NULL THEN newname
ELSE name
END name
FROM UNNEST(wives)
LEFT JOIN UNNEST(updates) ON t.name = u.name AND name = oldname
LEFT JOIN UNNEST(divorces) AS wifename ON t.name = d.name AND name = wifename
WHERE wifename IS NULL
) waves
FROM `project.dataset.table` t
LEFT JOIN (
SELECT name, ARRAY_AGG(STRUCT(oldname, newname)) updates
FROM updates GROUP BY name
) u ON t.name = u.name
LEFT JOIN (
SELECT name, ARRAY_AGG(wifename) divorces
FROM divorces GROUP BY name
) d ON t.name = d.name
result is as expected
name waves.age waves.name
adam 21 jen
rossi 21 polly
20 dessica
I hope you will be able to apply above to your real case :o)

SQL add examples from grouping data

Hello I have a small question in oracle SQL.
I have table Auto_Parts:
Category,Manufacturer_id,Part_name
Tires,Michelin, Pilot Pro
Tires,Michelin, Power One
Tires,Bridgestone, Potenza
Tires,Bridgestone, Turanza
Tires,Bridgestone, Blizzak
The query:
select Category,Manufacturer_id,count(*) cnt,example_1,example_2,example_3
from auto_parts
group by Category,Manufacturer_id
result:
Category,Manufacturer_id,cnt ,example_1,example_2,example_3
Tires ,Michelin ,1000 ,Pilot Pro,Power One,Power Two
Tires ,Bridgestone ,200 ,Potenza ,Turanza ,Blizzak
Question: how can I get 3 arbitrary values from the table above and present them as 3 columns in my query (a sample output is presented above, the columns are example_1,2,3)
This should do the trick. Obviously you don't need the WITH block, I just used that to mimic your data, so your query would start at the "select Category..."
with auto_parts as
(
select 'Tires' as Category,'Michelin' as Manufacturer_id, 'Pilot Pro' as part_name from dual union all
select 'Tires' as Category,'Michelin' as Manufacturer_id, 'Power One' as part_name from dual union all
select 'Tires' as Category,'Bridgestone' as Manufacturer_id, 'Potenza' as part_name from dual union all
select 'Tires' as Category,'Bridgestone' as Manufacturer_id, 'Turanza' as part_name from dual union all
select 'Tires' as Category,'Bridgestone' as Manufacturer_id, 'Blizzak' as part_name from dual
)
select
Category
Manufacturer_id,
count(*) cnt ,
max(case when rn = 1 then part_name end) example_1,
max(case when rn = 2 then part_name end) example_2,
max(case when rn = 3 then part_name end) example_3
from (
select category, manufacturer_id, part_name, row_number() over (partition by category, manufacturer_id order by dbms_random.value) rn
from auto_parts
)
group by Category,Manufacturer_id;