Return different rows for each column in a row - sql

I have data which is presented in multiple rows and columns with 0 or 1 values. What I'm trying to do is create a unique row for each 1, but there are sometimes multiple 1's in a row. For ex:
**A B C D**
1 0 1 1
0 0 0 1
1 1 0 0
I would like to have return six rows, all in one column like so
**RETURN**
A
C
D
D
A
B
Thanks in advance!

You can do this with a union all statement:
select val
from ((select 'A' as val from t where A = 1) union all
(select 'B' from t where B = 1) union all
(select 'C' from t where C = 1) union all
(select 'D' from t where D = 1)
) t
As a note: I hope you have other columns that you can include in the output. SQL tables are, by definition, not ordered. So, you really have no idea in your example of the original source for any given value.

Related

Add custom bool column where data is calculated based on values from linked entities

I have 2 tables: Entity and EntityItem.
EntityItems table has a Reason column which is nullable enum.
I'm trying to write a view that would return some Entititys columns and additionally a boolean column that states whether all corresponding EntityItem.Reason have a non-null value.
The following query returns somewhat what I want:
SELECT EntityItem.Id, COUNT(EntityItem.Reason) As Test
FROM EntityItem
GROUP BY EntityItem.ParentEntityId
ORDER BY Test DESC
Example output:
Id Test
132189 4
132190 2
132197 1
1 0
2 0
3 0
4 0
5 0
6 0
However, when I try to add this to a final query I get duplicated lines for each EntityItem
SELECT [Entity].[Id],
...
(SELECT CASE WHEN (SELECT COUNT([EntityItem].[Reason]) FROM [EntityItem] WHERE [EntityItem].[ParentEntityId] = [Entity].[Id]) = 0
THEN 0
ELSE 1
END) AS Test
FROM [Entity]
...
LEFT JOIN [EntityItem] ON [Entity].[Id] = [EntityItem].[ParentEntityId]
Example output:
Id Test
1 1
1 1
2 0
2 0
2 0
2 0
3 1
3 1
4 0
Question 1: Is my approach correct?
Question 2: Is there a way to remove duplicated lines without DISTINCT?
For your second query you need to aggregate before joining, for example by using outer apply something like:
select e.Id,
case when i.cnt = 0 then 0 else 1 end as Test
from Entity e
outer apply (
select Count(Reason) cnt
from EntityItem i
where i.ParentEntityId = e.Id
)i;
Saying that, since you are always returning a value of 1 if the count is greater than zero you don't actually need to count anything:
select e.Id,
case when exists (
select * from EntityItem i
where i.ParentEntityId = e.Id
)
then 1 else 0 end as Test
from Entity e;

Compare two columns in SQL

I'm new to SQL and have very basic queries in GCP.
Let's consider this table below:
Name
B
C
Arun
1234-5678
1234
Tara
6789 - 7654
6789
Arun
4567
4324
Here, I want to compare column B and C and if they match then give 1 else 0 in column same and else different (which we have to create).
So here the catch:
if column B has 1234-5678 and column C has 1234, then the column should match considering only the number before the "-" in the value.
The output should be :
Name
B
C
same
different
Arun
1234-5678
1234
1
0
Tara
6789 - 7654
6789
1
0
Arun
4567
4324
0
1
Also, I want to count the values of 1 for each values in Name for same and different columns.
So far I've tried this:
SELECT
name,
b,
c ,
if(b = c, 1, 0) as same,
if (b!=c,1,0) as different,
count(same),
count(different)
From Table
using "MySQL" (will work almost same with SQL server as well) here's the possible solution.
Step 1) Setup table
CREATE TABLE Users (
Name varchar(50),
B varchar(50),
C varchar(50)
);
INSERT INTO Users
VALUES
('Arun', '1234-5678', '1234'),
('Tara', '6789-7654', '6789'),
('Arun', '4567', '4324');
Step 2) same & different columns
SELECT
Name, B, C,
CASE WHEN SUBSTRING_INDEX(B, "-", 1) = C THEN 1 ELSE 0 END as same,
CASE WHEN SUBSTRING_INDEX(B, "-", 1) <> C THEN 1 ELSE 0 END as different
FROM
Users
Step 3) Join both results to get total_same & total_different for each user
SELECT
Name,
SUM(CASE WHEN SUBSTRING_INDEX(B, "-", 1) = C THEN 1 ELSE 0 END) as total_same,
SUM(CASE WHEN SUBSTRING_INDEX(B, "-", 1) <> C THEN 1 ELSE 0 END) as total_different
FROM
Users
GROUP BY Name
Reference: SQL Fiddle
For the first step, you will need to SUBSTR the column b.
We start at position 1 and we want 4 characters (only works if there's only 4 characters before the '-').
With table2 as (
select name, b,c, same, different from (select name, b, c, case when (SUBSTR(b,1,4) = c)
then '1' else '0' end as same, case when(SUBSTR(b,1,4)!= c) then '1' else '0' end as different
from Table1
group by name, b,c))
The WITH clause can be used when you have complex query, and if you want to create a temporary table in order to use it after.
The Table2 give you this :
After the WITH clause, you will have the second step, the count of same / different per name :
Select table1.name,count(table2.same+table2.different) as total from table1
join table2 on (table2.name = table1.name and table2.b = table1.b)
group by table1.name;
The output give you the total per name (the name are group by, so in your example you will only have 2 rows, one for Arun with a total of 2 (same + different) and the other one with a total of 1)
So here's the entire code :
with table2 as (
select name, b,c, same, different from (select name, b, c, case when (SUBSTR(b,1,4) = c) then '1' else '0' end as same, case when(SUBSTR(b,1,4)!= c) then '1' else '0' end as different
From Table1
group by name, b,c))
select table1.name, table1.b, table1.c, count(table2.same+table2.different) as total from table1
join table2 on (table2.name = table1.name and table2.b = table1.b)
group by table1.name;

Select rows using a count condition inside a where clause

There is a table, MyTable(ID, Type, Date). Column 'Type' can have a value of 1 or 2.
Top 'x' rows, ordered by 'Date' and satisfying the following condition, have to be selected.('a' and 'b' are integer values)
The selected 'x' rows can contain only a maximum of 'a' Type 1 rows and 'b' Type 2 rows.(If a+b < x, then only a+b rows have to be selected.)
I might be completely wrong but I have an idea of doing this by having count() inside a WHERE clause. But I am not sure of how to do it.
How do I go about this problem?
UPDATE:
Example -
x = 5
Case 1:
a = 5, b = 5
Result: Rows 1,2,3,4,5
Case 2:
a = 4, b = 1
Result: Rows 1,2,4,6,8
Case 3:
a = 1, b = 5
Result: Rows 1,2,3,5,7
Case 4:
a = 2, b = 1
Result: Rows 1,2,4
You can do it by nesting a UNION inside another query:
select top #x *
from (
select top #a *
from table
where type = '1'
order by [date]
union
select top #b *
from table
where type = '2'
order by [date]
) t
order by [date]

Select rows until condition met

I would like to write an Oracle query which returns a specific set of information. Using the table below, if given an id, it will return the id and value of B. Also, if B=T, it will return the next row as well. If that next row has a B=T, it will return that, and so on until a F is encountered.
So, given 3 it would just return one row: (3,F). Given 4 it would return 3 rows: ((4,T),(5,T),(6,F))
id B
1 F
2 F
3 F
4 T
5 T
6 F
7 T
8 F
Thank you in advance!
Use a sub-query to find out at what point you should stop, then return all row from your starting point to the calculated stop point.
SELECT
*
FROM
yourTable
WHERE
id >= 4
AND id <= (SELECT MIN(id) FROM yourTable WHERE b = 'F' AND id >= 4)
Note, this assumes that the last record is always an 'F'. You can deal with the last record being a 'T' using a COALESCE.
SELECT
*
FROM
yourTable
WHERE
id >= 4
AND id <= COALESCE(
(SELECT MIN(id) FROM yourTable WHERE b = 'F' AND id >= 4),
(SELECT MAX(id) FROM yourTable )
)

SQL (TSQL) - Select values in a column where another column is not null?

I will keep this simple- I would like to know if there is a good way to select all the values in a column when it never has a null in another column. For example.
A B
----- -----
1 7
2 7
NULL 7
4 9
1 9
2 9
From the above set I would just want 9 from B and not 7 because 7 has a NULL in A. Obviously I could wrap this as a subquery and USE the IN clause etc. but this is already part of a pretty unique set and am looking to keep this efficient.
I should note that for my purposes this would only be a one-way comparison... I would only be returning values in B and examining A.
I imagine there is an easy way to do this that I am missing, but being in the thick of things I don't see it right now.
You can do something like this:
select *
from t
where t.b not in (select b from t where a is null);
If you want only distinct b values, then you can do:
select b
from t
group by b
having sum(case when a is null then 1 else 0 end) = 0;
And, finally, you could use window functions:
select a, b
from (select t.*,
sum(case when a is null then 1 else 0 end) over (partition by b) as NullCnt
from t
) t
where NullCnt = 0;
The query below will only output one column in the final result. The records are grouped by column B and test if the record is null or not. When the record is null, the value for the group will increment each time by 1. The HAVING clause filters only the group which has a value of 0.
SELECT B
FROM TableName
GROUP BY B
HAVING SUM(CASE WHEN A IS NULL THEN 1 ELSE 0 END) = 0
If you want to get all the rows from the records, you can use join.
SELECT a.*
FROM TableName a
INNER JOIN
(
SELECT B
FROM TableName
GROUP BY B
HAVING SUM(CASE WHEN A IS NULL THEN 1 ELSE 0 END) = 0
) b ON a.b = b.b