How to copy column names if a column equal to something - sql

I want to
If a column is 1, then copy the column name (to a new column). For example, for ID 1, the Name1 is 1, then we copy 'Name1' (to the 'Name' column). Else, do nothing.
If two columns (Name1, Name2) are both 1, then we will have two rows for each name. For example, ID 3.
Input
ID Name1 Name2
1 1 0
2 0 1
3 1 1
Output
ID Name
1 Name1
2 Name2
3 Name1
3 Name2
Do I need some advanced keywords to do that?

You should be able to use the UNPIVOT function to get the result. This converts your columns into rows, then you can filter the final result based on whether the value of the original column is 0 or 1:
select Id, Name
from <yourtable>
unpivot
(
value for
name in (Name1, Name2)
) u
where value <> 0
Here is a demo

One way is using union all
select id,
'Name1' as name
from your_table
where name1 = 1
union all
select id,
'Name2' as name
from your_table
where name2 = 1
You could also use cross apply if there are more columns:
select t.id, x.name
from your_table t
cross apply (
values (case when t.name1 = 1 then 'Name1' end),
(case when t.name2 = 1 then 'Name2' end),
(case when t.name3 = 1 then 'Name3' end)
) x (name)
where x.name is not null;
Demo

You can use cross apply and get this as below:
select Id, nam as [Name] from #yournames
cross apply ( values (name1, 'name1'),(name2, 'name2')) v(n, nam)
where n = 1
Output:
+----+-------+
| Id | Name |
+----+-------+
| 1 | name1 |
| 2 | name2 |
| 3 | name1 |
| 3 | name2 |
+----+-------+

If there are only 3 columns, use union
select id, 'Name1' as Name from Input where Name1=1
union all
select id, 'Name2' as Name from Input where Name2=1

Related

How can I count the total no of rows which is not containing some value in array field? It should include null values as well

| names |
| -----------------------------------|
| null |
| null |
| [{name:'test'},{name:'test1'}] |
| [{name:'test'},{name:'test1'}] |
| [{name:'test1'},{name:'test2'}] |
I want to count the no of rows which does not have the value 'test' in the name key.
Here it should give answer as 3 (Row no 1, 2 and 5th row) because all these row do not contain the value 'test'.
Use below approach
select count(*)
from your_table
where 0 = ifnull(array_length(regexp_extract_all(names, r"\b(name:'test')")), 0)
you can test it with below data (that resemble whatever you presented in your question)
with your_table as (
select null names union all
select null union all
select "[{name:'test'},{name:'test1'}]" union all
select "[{name:'test'},{name:'test1'}]" union all
select "[{name:'test1'},{name:'test2'}]"
)
with output
Below approach will work,
with your_table as (
select null names union all
select null union all
select [('name','test'),('name','test1')] union all
select [('name','test'),('name','test1')] union all
select [('name','test1'),('name','test2')]
)
SELECT COUNT(*) as count FROM your_table
WHERE NOT EXISTS (
SELECT 1
FROM UNNEST(your_table.names) AS names
WHERE names IN (('name','test'))
)

Oracle - how to join multiple rows in vertical oriented table

Let's say I have two tables like those:
table1:
-----------------
| someId | value|
|--------|------|
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
-----------------
table2:
-----------------------------------
| someId | type | value1 | value2 |
|--------|------|--------|--------|
| 1 | 2 | hello | |
| 1 | 3 | | 2 |
| 1 | 4 | | |
| 2 | 4 | | |
-----------------------------------
table1.someId = table2.someId
table2 is vertical, so multiple rows of this table (based on someId) refer to table1.someId.
Now I need to obtain count of rows from table1 for which table1.value=? AND (table2.type=2 andtable2.value1=?) AND (table2.type=3 and table2.value2=?) joined on table1.someId = table2.someId.
This is the query I have right now (it is parametrized and parameters for value, value1 and value2 are passed from a client):
select count(case when t1.value = ? then 1 end) from table1 t1
inner join
(select value1.someId from
(select someId from table2 where type = 2 and value1 = ?) value1
inner join
(select someId from table2 where type = 3 and value2 = ?) value2
on value1.someId = value2.someId
) t2
on t1.someId = t2.someId;
Example query:
select count(case when t1.value = 2 then 1 end) from table1 t1
inner join
(select value1.someId from
(select someId from table2 where type = 2 and value1 ='hello') value1
inner join
(select someId from table2 where type = 3 and value2 = 2) value2
on value1.someId = value2.someId
) t2
on t1.someId = t2.someId;
Is there any other way how to achieve this instead of multiple selects joined by inner joins? (In reality, I have to search by three types from table2).
Running example with correct result (updated example from Michael Buen):
db-fiddle.com
Thank you.
What you need is write a custom pivot for table2 groping by someid before join with table1:
with s (someId, type, value1, value2) as (
select 1, 2, 'hello', to_number(null) from dual union all
select 1, 3, null , 2 from dual union all
select 1, 4, null , null from dual union all
select 2, 4, null , null from dual)
select someid,
max(case when type = 2 then value1 end) type2_value1,
max(case when type = 3 then value2 end) type3_value2/*,
max(case when type = 4 then value1 end) type4_value1
max(case when type = 4 then value2 end) type4_value2*/
from s
group by someid;
SOMEID TYPE2 TYPE3_VALUE2
---------- ----- ------------
1 hello 2
2

SQL theory: Filtering out duplicates in one column, picking lowest value in other column

I am trying to figure out the best way to remove rows from a result set where either the value in one column or the value in a different column has a duplicate in the result set.
Imagine the results of a query are as follows:
a_value | b_value
-----------------
1 | 1
2 | 1
2 | 2
3 | 1
4 | 3
5 | 2
6 | 4
6 | 5
What I want to do is:
Eliminate all rows that have duplicate values in a_value
Pick only 1 row for a given b_value
So I'd want the filtered results to end up like this after eliminating a_value duplicates:
a_value | b_value
-----------------
1 | 1
3 | 1
4 | 3
5 | 2
And then like this after picking only a single b_value:
a_value | b_value
-----------------
1 | 1
4 | 3
5 | 2
I'd appreciate suggestions on how to accomplish this task in an efficient way via SQL.
with
q_res ( a_value, b_value ) as (
select 1, 1 from dual union all
select 2, 1 from dual union all
select 2, 2 from dual union all
select 3, 1 from dual union all
select 4, 3 from dual union all
select 5, 2 from dual union all
select 6, 4 from dual union all
select 6, 5 from dual
)
-- end test data; solution begins below
select min(a_value) as a_value, b_value
from (
select a_value, min(b_value) as b_value
from q_res
group by a_value
having count(*) = 1
)
group by b_value
order by a_value -- ORDER BY is optional
;
A_VALUE B_VALUE
------- -------
1 1
4 3
5 2
1) In the inner query I am avoiding all duplicates which are present in a_value
column and getting all the remaining rows from input table and storing them
as t2. By joining t2 with t1 there would be full data without any dups as per
your #1 in requirement.
SELECT t1.*
FROM Table t1,
(
SELECT a_value
FROM Table
GROUP BY a_value
HAVING COUNT(*) = 1
) t2
WHERE t1.a_value = t2.a_value;
2) Once the filtered data is obtained, I am assigning rank to each row in the filtered dataset obtained in step-1 and I am selecting only rows with rank=1.
SELECT X.a_value,
X.b_value
FROM
(
SELECT t1.*,
ROW_NUMBER() OVER ( PARTITION BY t1.b_value ORDER BY t1.a_value,t1.b_value ) AS rn
FROM Table t1,
(
SELECT a_value
FROM Table
GROUP BY a_value
HAVING COUNT(*) = 1
) t2
WHERE t1.a_value = t2.a_value
) X
WHERE X.rn = 1;

Using Case in a select statement

Consider the following table
create table temp (id int, attribute varchar(25), value varchar(25))
And values into the table
insert into temp select 100, 'First', 234
insert into temp select 100, 'Second', 512
insert into temp select 100, 'Third', 320
insert into temp select 101, 'Second', 512
insert into temp select 101, 'Third', 320
I have to deduce a column EndResult which is dependent on 'attribute' column. For each id, I have to parse through attribute values in the order
First, Second, Third and choose the very 1st value which is available i.e. for id = 100, EndResult should be 234 for the 1st three records.
Expected result:
| id | EndResult |
|-----|-----------|
| 100 | 234 |
| 100 | 234 |
| 100 | 234 |
| 101 | 512 |
| 101 | 512 |
I tried with the following query in vain:
select id, case when isnull(attribute,'') = 'First'
then value
when isnull(attribute,'') = 'Second'
then value
when isnull(attribute,'') = 'Third'
then value
else '' end as EndResult
from
temp
Result
| id | EndResult |
|-----|-----------|
| 100 | 234 |
| 100 | 512 |
| 100 | 320 |
| 101 | 512 |
| 101 | 320 |
Please suggest if there's a way to get the expected result.
You can use analytical function like dense_rank to generate a numbering, and then select those rows that have the number '1':
select
x.id,
x.attribute,
x.value
from
(select
t.id,
t.attribute,
t.value,
dense_rank() over (partition by t.id order by t.attribute) as priority
from
Temp t) x
where
x.priority = 1
In your case, you can conveniently order by t.attribute, since their alphabetical order happens to be the right order. In other situations you could convert the attribute to a number using a case, like:
order by
case t.attribute
when 'One' then 1
when 'Two' then 2
when 'Three' then 3
end
In case the attribute column have different values which are not in alphabetical order as is the case above you can write as:
with cte as
(
select id,
attribute,
value,
case attribute when 'First' then 1
when 'Second' then 2
when 'Third' then 3 end as seq_no
from temp
)
, cte2 as
(
select id,
attribute,
value,
row_number() over ( partition by id order by seq_no asc) as rownum
from cte
)
select T.id,C.value as EndResult
from temp T
join cte2 C on T.id = C.id and C.rownum = 1
DEMO
Here is how you can achieve this using ROW_NUMBER():
WITH t
AS (
SELECT *
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY (CASE attribute WHEN 'First' THEN 1
WHEN 'Second' THEN 2
WHEN 'Third' THEN 3
ELSE 0 END)
) rownum
FROM TEMP
)
SELECT id
,(
SELECT value
FROM t t1
WHERE t1.id = t.id
AND rownum = 1
) end_result
FROM t;
For testing purpose, please see SQL Fiddle demo here:
SQL Fiddle Example
keep it simple
;with cte as
(
select row_number() over (partition by id order by (select 1)) row_num, id, value
from temp
)
select t1.id, t2.value
from temp t1
left join cte t2
on t1.Id = t2.id
where t2.row_num = 1
Result
id value
100 234
100 234
100 234
101 512
101 512

Ordinal numbers in select

First sorry for my bad english it's not my native language :(
I'm kinda new in Oracle and I need help with following. I have several records with same ID, several values (which can be same) and different creation date.
I would like to select an ordinal number for IDs which have same value, but different date.
For example
ID | Value | Date | Number
A | Value1 | 01.11. | 1
A | Value1 | 02.11. | 2
A | Value2 | 03.11. | null
A | Value2 | 01.11. | null
B | Value1 | 01.11. | 1
B | Value1 | 03.11. | 2
B | Value2 | 01.11. | null
C | Value1 | 01.11. | 1
C | Value2 | 01.11. | null
So for every ID in first coloumn where I have Value1 I want to have increment and for the rest of the values I don't need to have anything.
I hope I'm not posting double question I have tried to look it up, but I couldn't find any answer.
Thank you in advance!
Edit: Will accept one instead of null for other values.
The basic idea is row_number() to get the sequential value and rank() to rank the values. You only want the first set to be enumerated. "First" corresponds to rank() having a value of 1. The rest get NULL:
select id, value, date,
(case when rank() over (partition by id order by value) = 1
then row_number() over (partition by id order by value)
end) as number
from table t;
EDIT:
I realize that you might actually want the first value by time and not some other ordering. For that, use keep instead of rank():
select id, value, date,
(case when value = max(value) keep (dense_rank first order by value) over (partition by id)
then row_number() over (partition by id order by value)
end) as number
from table t;
Hmm... Hope I understood correctly:
with my_table as (
select 'A' ID, 'Value1' value, '01.11.' dt, 1 num from dual union all
select 'A', 'Value1', '02.11.', 2 from dual union all
select 'A', 'Value2', '03.11.', null from dual union all
select 'A', 'Value2', '01.11.', null from dual union all
select 'B', 'Value1', '01.11.', 1 from dual union all
select 'B', 'Value1', '03.11.', 2 from dual union all
select 'B', 'Value2', '01.11.', null from dual union all
select 'C', 'Value1', '01.11.', 1 from dual union all
select 'C', 'Value2', '01.11.', null from dual)
select *
from (select t.*, count(distinct dt) over (partition by value, id) diff_cnt
from my_table t) tt
where tt.diff_cnt > 1;
result:
ID VALUE DT NUM DIFF_CNT
-- ------ ------ ---------- ----------
A Value1 01.11. 1 2
A Value1 02.11. 2 2
B Value1 01.11. 1 2
B Value1 03.11. 2 2
A Value2 01.11. 2
A Value2 03.11. 2