Combine results from separate columns grouped by a specific column - sql

having a table like this in oracle db:
+-----+-----+-----+-----+
| VAL1| VAL2| VAL3| ID |
+-----+-----+-----+-----+
| A | | | 1 |
-------------------------
| | B | | 1 |
-------------------------
| | | C | 1 |
-------------------------
| X | | Z | 2 |
-------------------------
| | Y | | 2 |
-------------------------
| | Y | Z | 2 |
-------------------------
| E | | | 3 |
-------------------------
| | | F | 3 |
-------------------------
How to select from this table (in ORACLE) in order to get following output where all this rows are grouped by ID column?
+-----+-----+-----+-----+
| VAL1| VAL2| VAL3| ID |
+-----+-----+-----+-----+
| A | B | C | 1 |
-------------------------
| X | Y | Z | 2 |
-------------------------
| E | | F | 3 |
-------------------------

You can use aggregation:
select id, max(val1) as val1, max(val2) as val2, max(val3) as val3
from t
group by id;

Use GROUP BY combined with an aggregation function, such as MAX().
For example:
select
max(val1) as val1,
max(val2) as val2,
max(val3) as val3,
id
from t
group by id

Related

SQL return only rows where value exists multiple times and other value is present

I have a table like this in MS SQL SERVER
+------+------+
| ID | Cust |
+------+------+
| 1 | A |
| 1 | A |
| 1 | B |
| 1 | B |
| 2 | A |
| 2 | A |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 3 | B |
| 3 | C |
| 3 | C |
+------+------+
I don't know the values in column "Cust" and I want to return all rows where the value of "Cust" appears multiple times and where at least one of the "ID" values is "1".
Like this:
+------+------+
| ID | Cust |
+------+------+
| 1 | A |
| 1 | A |
| 1 | B |
| 1 | B |
| 2 | A |
| 2 | A |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 3 | B |
+------+------+
Any ideas? I can't find it.
You may use COUNT window function as the following:
SELECT ID, Cust
FROM
(
SELECT ID, Cust,
COUNT(*) OVER (PARTITION BY Cust) cn,
COUNT(CASE WHEN ID=1 THEN 1 END) OVER (PARTITION BY Cust) cn2
FROM table_name
) T
WHERE cn>1 AND cn2>0
ORDER BY ID, Cust
COUNT(*) OVER (PARTITION BY Cust) to check if the value of "Cust" appears multiple times.
COUNT(CASE WHEN ID=1 THEN 1 END) OVER (PARTITION BY Cust) to check that at least one of the "ID" values is "1".
See a demo.

Inserting set of rows for every ID in another table

this is an initial table (this is just a part of a larger table where Article ID's can vary), database is MS Sql.
-----------------------------------
|ArticleID | GroupID |
-----------------------------------
| 1 | NULL |
-----------------------------------
| 2 | NULL |
-----------------------------------
| 3 | NULL |
-----------------------------------
| 4 | NULL |
-----------------------------------
Set of rows that should be entered for each ArticleID looks something like this:
------------------------
| GroupID |
------------------------
| A |
------------------------
| B |
------------------------
| C |
------------------------
| D |
------------------------
Result table should look something like this:
-----------------------------------
|ArticleID | GroupID |
-----------------------------------
| 1 | NULL |
-----------------------------------
| 1 | A |
-----------------------------------
| 1 | B |
-----------------------------------
| 1 | C |
-----------------------------------
| 1 | D |
-----------------------------------
| 2 | NULL |
-----------------------------------
| 2 | A |
-----------------------------------
| 2 | B |
-----------------------------------
| 2 | C |
-----------------------------------
| 2 | D |
-----------------------------------
| 3 | NULL |
-----------------------------------
| 3 | A |
-----------------------------------
| 3 | B |
-----------------------------------
| 3 | C |
-----------------------------------
| 3 | D |
-----------------------------------
| 4 | NULL |
-----------------------------------
| 4 | A |
-----------------------------------
| 4 | B |
-----------------------------------
| 4 | C |
-----------------------------------
| 4 | D |
-----------------------------------
Any suggestion how to insert it efficiently?
Thanks a lot for you suggestion.
Regards
This is a cross join between two sets.
with a as (
select * from(values (1),(2),(3),(4))v(ArticleId)
), g as (
select * from(values (null),('A'),('B'),('C'),('D'))v(GroupId)
)
select *
from a cross join g;
To insert into the original table you could do:
with g as (select * from(values('A'),('B'),('C'),('D'))v(GroupId))
insert into t
select t.ArticleId, g.GroupId
from t cross join g;
See Example Fiddle

Boolean Condition Group By, Window Functions

I am trying to add a new column based on a condition in a group.
Could we do something like
BOOL() OVER (PARTITION BY id 'D' in val)
That is something like GROUP BY id and check if the value 'D" val column
Input:
-------------
| id | val |
------------|
| 1 | A |
| 1 | B |
| 1 | D |
| 2 | B |
| 2 | C |
| 2 | A |
Output
-------------------
| id | val | res |
------------|-----|
| 1 | A | 1 |
| 1 | B | 1 |
| 1 | D | 1 |
| 2 | B | 0 |
| 2 | C | 0 |
| 2 | A | 0 |
You didn't specify your DBMS, but in standard ANSI SQL, you can use a filter() clause:
count(*) filter (where val = 'D') over (partition by id) > 0
In Postgres, you can use bool_or() for this:
select t.*, bool_or(val = 'D') over(partition by id) res
from mytable t
Demo on DB Fiddle:
id | val | res
-: | :-- | :--
1 | A | t
1 | B | t
1 | D | t
2 | B | f
2 | C | f
2 | A | f
This gives you a boolean result. If you want it as an integer value instead, then:
(bool_or(val = 'D') over(partition by id))::int

Query to group 5 records

I have table for eg "employee" with just one column "id". Say you have records from 1 through 1000.
Employee
------------
ID
------------
1
2
3
..
..
999
1000
Now I would like to write a query which gives the following results i.e. sort by ascending order and concatenate first 5 to 1 record, second 5 to 2 second, and so on. Any ideas how I can do this?
Here is the output I am looking to have.
1,2,3,4,5
6,7,8,9,10
11,12,13,14,15
...........
...........
996,997,998,999,1000
Use row_number and listagg functions, in this way:
SELECT listagg( id, ',' ) within group( order by group_no, id )
FROM (
select id,
trunc((row_number() over( order by id ) -1) / 5) as group_no
from employee
)
GROUP BY group_no
Working demo: http://sqlfiddle.com/#!4/ef526/10
| LISTAGG(ID,',')WITHINGROUP(ORDERBYGROUP_NO,ID) |
|------------------------------------------------|
| 1,2,3,4,5 |
| 6,7,8,9,10 |
| 11,12,13,14,15 |
| 16,17,18,19,20 |
| 21,22,23,24,25 |
| 26,27,28,29,30 |
| 31,32,33,34,35 |
| 36,37,38,39,40 |
| 41,42,43,44,45 |
| 46,47,48,49,50 |
| 51,52,53,54,55 |
| 56,57,58,59,60 |
| 61,62,63,64,65 |
| 66,67,68,69,70 |
| 71,72,73,74,75 |
| 76,77,78,79,80 |
| 81,82,83,84,85 |
| 86,87,88,89,90 |
| 91,92,93,94,95 |
| 96,97,98,99,100 |
| 101,102,103,104,105 |
| 106,107,108,109,110 |
| 111,112,113,114,115 |
| 116,117,118,119,120 |
| 121,122,123,124,125 |
| 126,127,128,129,130 |
| 131,132,133,134,135 |
| 136,137,138,139,140 |
| 141,142,143,144,145 |
| 146,147,148,149,150 |
| 151,152,153,154,155 |
| 156,157,158,159,160 |
| 161,162,163,164,165 |
| 166,167,168,169,170 |
| 171,172,173,174,175 |
| 176,177,178,179,180 |
| 181,182,183,184,185 |
| 186,187,188,189,190 |
| 191,192,193,194,195 |
| 196,197,198,199,200 |

SQL all rows into one column - no concatenation

I have a table that I need has multiple rows and I to put them all into a new table.
All my rows need to be converted into one row.
+-------+-----------+-------+--------+
| ID | Name | Last | Gender |
+-------+-----------+-------+--------+
| 1 | Person1 | Last1 | M |
| 2 | Person2 | Last2 | F |
| 3 | Person3 | Last3 | M |
| 4 | Person4 | Last4 | F |
+-------+-----------+-------+--------+
I need to convert the above table to the below:
+-------+------------+------------+
| NewID | ColumnName | Value |
+-------+------------+------------+
| 1 | ID | 1 |
| 1 | Name | Person1 |
| 1 | Last | Last1 |
| 1 | Gender | M |
| 2 | ID | 2 |
| 2 | Name | Person2 |
| 2 | Last | Last2 |
| 2 | Gender | F |
| 3 | ID | 3 |
| 3 | Name | Person3 |
| 3 | Last | Last3 |
| 3 | Gender | M |
| 4 | ID | 4 |
| 4 | Name | Person4 |
| 4 | Last | Last4 |
| 4 | Gender | F |
| | | |
+-------+------------+------------+
The most general method is to use union all:
select 'id' as columnname, cast(id as varchar(255)) as value from t union all
select 'name', name as value from t union all
select 'last', last as value from t union all
select 'gender', gender as value from t;
This should work in basically any database, although the cast to a string might vary. Some databases offer other solutions that are more efficient.
Union happy solution.
select 'id' as columnname, id as value from table
union all
select 'name' as columnname, name as value from table
union all
.e
.t
.c