how to get value x without code duplication - sql

create table t(a int, b int);
insert into t values (1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3);
select * from t;
a | b
----------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
3 | 3
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c,
(
max(case when a = 1 then b else 0 end)
+
max(case when b = 1 then a else 0 end)
) as x
from t
Is it possible to do something like this?
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c,
(q + c) as x
from t

You can't use the ALIAS that was given on the same level of the SELECT clause.
You have two choices:
by using the expression directly
query:
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c,
(max(case when a = 1 then b else 0 end) + max(case when b = 1 then a else 0 end)) as x
from t
by wrapping in a subquery
query:
SELECT q,
c,
q + c as x
FROM
(
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c
from t
) d

Also in SQLServer2005+ you can use CTE
;WITH cte AS
(
select max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c
from t
)
SELECT q, c, q + c as x
FROM cte

You can't do that unfortunately.
The ALIAS can not be used in the same level where you created them.
A temporary table is necessary, i think.

Related

How to Replace NULL Value with 0 (Zero)?

I've just got myself stuck with some SQL query and I'm quite new on this.
I'm using pivot in my query.
This is my SELECT query:
SELECT *
FROM
(SELECT lg.domainNameID AS [Domain ID], COUNT(lg.domainNameID) AS [Fix Count]
FROM tbl_ATT_Request r
INNER JOIN tbl_ATT_Login lg ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID) slct
and this is the output:
Domain | Fix Count
-------+-----------
1 1
2 1
4 2
5 1
And this is my query with PIVOT.
SELECT *
FROM
(SELECT lg.domainNameID AS [Domain ID], COUNT(lg.domainNameID) AS [Fix Count]
FROM tbl_ATT_Request r
INNER JOIN tbl_ATT_Login lg ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID) slct
PIVOT
(SUM(slct.[Fix Count])
FOR slct.[Domain ID] IN ([1],[2],[3],[4],[5])
) AS pvt
This is the output:
1 | 2 | 3 | 4 | 5
1 1 NULL 2 1
Now my problem is how can I replace the NULL values with 0.
Just use conditional aggregation:
SELECT SUM(CASE WHEN Domain_Id = 1 THEN Fix_Count ELSE 0 END) as d_1,
SUM(CASE WHEN Domain_Id = 2 THEN Fix_Count ELSE 0 END) as d_2,
SUM(CASE WHEN Domain_Id = 3 THEN Fix_Count ELSE 0 END) as d_3,
SUM(CASE WHEN Domain_Id = 4 THEN Fix_Count ELSE 0 END) as d_4,
SUM(CASE WHEN Domain_Id = 5 THEN Fix_Count ELSE 0 END) as d_5
FROM (SELECT lg.domainNameID AS Domain_ID, COUNT(*) AS Fix_Count
FROM tbl_ATT_Request r JOIN
tbl_ATT_Login lg
ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID
) d

How to use count distinct along multiple columns?

Input:
id sem1 sem2 sem3 sem4 sem5 sem6 sem7
1 S O S R null null null
2 O O R R S null null
Desired Output:
id O R S
1 1 1 2
2 2 2 1
If your database supports APPLY/UNPIVOT operator then use this
CROSS APPLY method
SELECT id,
SUM(CASE WHEN val = 'O' THEN 1 ELSE 0 END) O,
SUM(CASE WHEN val = 'R' THEN 1 ELSE 0 END) R,
SUM(CASE WHEN val = 'S' THEN 1 ELSE 0 END) S
FROM mytable
CROSS apply (VALUES (sem1),
(sem2),
(sem3),
(sem4),
(sem5),
(sem6),
(sem7)) cs(val)
GROUP BY id
SQL FIDDLE DEMO
UNPIVOT method
SELECT id,
SUM(CASE WHEN val = 'O' THEN 1 ELSE 0 END) O,
SUM(CASE WHEN val = 'R' THEN 1 ELSE 0 END) R,
SUM(CASE WHEN val = 'S' THEN 1 ELSE 0 END) S
FROM (SELECT *
FROM mytable) a
UNPIVOT (val
FOR col IN ( sem1,
sem2,
sem3,
sem4,
sem5,
sem6,
sem7 )) upv
GROUP BY id
SQL FIDDLE DEMO
I personally prefer CROSS APPLY method over UNPIVOT since it is more readable. Performance wise both will be identical

SQL check if column contains specific values

I have a table like this:
id | Values
------------------
1 | a
1 | b
1 | c
1 | d
1 | e
2 | a
2 | a
2 | c
2 | c
2 | e
3 | a
3 | c
3 | b
3 | d
Now I want to know which id contains at least one of a, one of b and one of c.
This is the result I want:
id
--------
1
3
One method is aggregation with having:
select id
from t
where values in ('a', 'b', 'c')
group by id
having count(distinct values) = 3;
If you wanted more flexibility with the counts of each value:
having sum(case when values = 'a' then 1 else 0 end) >= 1 and
sum(case when values = 'b' then 1 else 0 end) >= 1 and
sum(case when values = 'c' then 1 else 0 end) >= 1
You can use grouping:
SELECT id
FROM your_table
GROUP BY id
HAVING SUM(CASE WHEN value = 'a' THEN 1 ELSE 0 END) >= 1
AND SUM(CASE WHEN value = 'b' THEN 1 ELSE 0 END) = 1
AND SUM(CASE WHEN value = 'c' THEN 1 ELSE 0 END) = 1;
or using COUNT:
SELECT id
FROM your_table
GROUP BY id
HAVING COUNT(CASE WHEN value = 'a' THEN 1 END) >= 1
AND COUNT(CASE WHEN value = 'b' THEN 1 END) = 1
AND COUNT(CASE WHEN value = 'c' THEN 1 END) = 1;

Build String depending on Column values [Oracle SQL]

I have this table:
ID | UNIT| CODE
2 | A | bit0
2 | A | bit2
1 | B | bit2
2 | B | bit7
1 | B | bit5
1 | C | bit7
I wonder how can I group the bits depending on the ID and UNIT? For example, the output for the source table above would be:
ID|UNIT| CODE
2 | A |00000101
1 | B |00100100
1 | C |10000000
2 | B |10000000
is a CASE statement + concatenating 1's and 0's the best option here? I really don't think so but that's the only solution I can find at the moment.
Thanks!
You could use the bit_or aggregation function in MySQL, but it is not available in Oracle.
If you never have more than one bit set, you could do
select id, unit,
sum(case when code = 'bit0' then 1
when code = 'bit1' then 2
when code = 'bit2' then 4
when code = 'bit3' then 8
when code = 'bit4' then 16
when code = 'bit5' then 32
when code = 'bit6' then 64
when code = 'bit7' then 128
else 0
end)
from table t
group by id, unit;
But that's not really a satisfying answer.
Instead, you need to spread the values out, aggregate, and bring them back. Here is a method where the result is a string:
select id, unit,
(max(case when code = 'bit0' then 1 else 0 end) ||
max(case when code = 'bit1' then 1 else 0 end) ||
max(case when code = 'bit2' then 1 else 0 end) ||
max(case when code = 'bit3' then 1 else 0 end) ||
max(case when code = 'bit4' then 1 else 0 end) ||
max(case when code = 'bit5' then 1 else 0 end) ||
max(case when code = 'bit6' then 1 else 0 end) ||
max(case when code = 'bit7' then 1 else 0 end)
)
from table t
group by id, unit;
And here is the method with the result as an integer:
select id, unit,
(max(case when code = 'bit0' then 1 else 0 end) +
max(case when code = 'bit1' then 2 else 0 end) +
max(case when code = 'bit2' then 4 else 0 end) +
max(case when code = 'bit3' then 8 else 0 end) +
max(case when code = 'bit4' then 16 else 0 end) +
max(case when code = 'bit5' then 32 else 0 end) +
max(case when code = 'bit6' then 64 else 0 end) +
max(case when code = 'bit7' then 128 else 0 end)
)
from table t
group by id, unit;
You can see the latter work here at SQL Fiddle.

Get Data Of Third Column Based on Two other Columns

This is a sample table
ID STOREA STOREB STOREC AB BC CA ABC
--- ------- ------ ------- -- -- --- ---
10 1 0 0
10 0 1 0
10 0 1 0
29 0 1 0
29 0 0 1
29 1 0 0
Each row corresponds to a purchase made at either of Store A or B or C. Customer 10 shops at A and B but not c. So I want AB=1 BC=0 CA=0 ABC=0 for all ID=10 rows and for ID=29, he shops at all 3, so I need AB=1 BC=1 CA=1 ABC=1 for all rows where ID=29 (using ORACLE SQL)
I would like to update the columns in the table.
Here is one way you can do this. I don't think you can use JOINs in Oracle with UPDATE statements -- however, you can accomplish the same thing by using MERGE:
MERGE
INTO yourtable
USING (
select id as idnew,
case when a + b = 2 then 1 else 0 end abnew,
case when b + c = 2 then 1 else 0 end bcnew,
case when a + c = 2 then 1 else 0 end acnew,
case when a + b + c = 3 then 1 else 0 end abcnew
from (
select
id,
max(case storea when 1 then 1 else 0 end) A,
max(case storeb when 1 then 1 else 0 end) B,
max(case storec when 1 then 1 else 0 end) C
from yourtable
group by id
) a
)
ON (id = idnew)
WHEN MATCHED THEN
UPDATE
SET ab = abnew,
bc = bcnew,
ac = acnew,
abc = abcnew
SQL Fiddle Demo
Here is how you can do this as a select:
update (select id, storea, storeb, storec, AB as new_AB, BC as new_BC, AC as new_AC, ABC as new_ABC
from t join
(select id,
(case when max(storeA) = 1 and max(storeB) = 1 then 1 else 0 end) as AB,
(case when max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as BC,
(case when max(storeA) = 1 and max(storeC) = 1 then 1 else 0 end) as AC,
(case when max(storeA) = 1 and max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as ABC
from t
group by id
) tsum
on t.id = tsum.id
)
set AB = new_AB, AC = new_AC, BC = new_BC, ABC = new_ABC;
I think this might work:
select id, storea, storeb, storec, AB, BC, AC, ABC
from t join
(select id,
(case when max(storeA) = 1 and max(storeB) = 1 then 1 else 0 end) as AB,
(case when max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as BC,
(case when max(storeA) = 1 and max(storeC) = 1 then 1 else 0 end) as AC,
(case when max(storeA) = 1 and max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as ABC
from t
group by id
) tsum
on t.id = tsum.id
)
set AB = new_AB, AC = new_AC, BC = new_BC, ABC = new_ABC;