Build String depending on Column values [Oracle SQL] - 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.

Related

how to sum count sql

How to total count?
SELECT
COUNT(CASE WHEN SHP.id = 1 then 1 ELSE NULL END) as "New",
COUNT(CASE WHEN SHP.id = 2 then 5 ELSE NULL END) as "Accepted"
from SHP
RESULT:
NEW Accepted
1 5
But I need a total count
result: 6
I'd do something like this;
SELECT
COUNT(CASE WHEN id = 1 THEN 1 END) as New,
COUNT(CASE WHEN id = 2 THEN 5 END) as Accepted,
COUNT(CASE WHEN id = 1 THEN 1
WHEN id = 2 THEN 5 END) as Total
FROM SHP
This is exactly what the CASE statement should be used for, the logic is very simple. This will avoid having to perform multiple calculations on the same fields.
As a note, the value in your THEN statement isn't used in this instance at all, it's just doing a COUNT of the number rather than performing a SUM. I've also removed the ELSE NULL because this is what the CASE will do by default anyway.
If your intention was to SUM the values then do this;
SELECT
SUM(CASE WHEN id = 1 THEN 1 END) as New,
SUM(CASE WHEN id = 2 THEN 5 END) as Accepted,
SUM(CASE WHEN id = 1 THEN 1
WHEN id = 2 THEN 5 END) as Total
FROM SHP
Example
Assuming you have only two values in your database, 1 and 2, we can create test data like this;
CREATE TABLE #SHP (id int)
INSERT INTO #SHP (id)
VALUES (1),(2)
And use this query;
SELECT
SUM(CASE WHEN id = 1 then 1 END) as New,
SUM(CASE WHEN id = 2 then 5 END) as Accepted,
SUM(CASE WHEN id = 1 THEN 1
WHEN id = 2 THEN 5 END) as Total
FROM #SHP
Gives this result;
New Accepted Total
1 5 6
Try this:
SELECT
COUNT(CASE WHEN SHP.id = 1 then 1 ELSE NULL END) +
COUNT(CASE WHEN SHP.id = 2 then 5 ELSE NULL END) as "Total"
from SHP
You could wrap your query into a subquery and do something like this:
SELECT SUM(New) as New, Sum(Accepted) as Accepted, Sum(New + Accepted) as Total FROM
(SELECT
COUNT(CASE WHEN SHP.id = 1 then 1 ELSE NULL END) as "New",
COUNT(CASE WHEN SHP.id = 2 then 5 ELSE NULL END) as "Accepted"
from SHP) as SubQuery
That's if you don't want to duplicate doing the counts and just adding the two together.
try this
with s1 as(
SELECT
COUNT(CASE WHEN SHP.id = 1 then 1 ELSE 0 END) as "New"
from SHP
),s2 as
(
SELECT
COUNT(CASE WHEN SHP.id = 2 then 5 ELSE 0 END) as "Accepted"
from SHP
)
select sum("New"+ "Accepted") from s1,s2

SQL separate the count of one column

I have a SQL table that contains three columns:
userId
userName
item
and I created this SQL query which will count all the items types of one user:
select
count(ItemID) as 'count of all items types',
userId,
userName
from
userTable
where
ItemID in (2, 3, 4)
and userId = 1
group by
userId, userName
The result will be like this:
+--------+----------+--------------------------+
| userId | userName | count of all items types |
+--------+----------+--------------------------+
| 1 | kim | 25 |
and I am looking for a way to separate the counting of itemes types, so the result should be like this:
+--------+----------+----------------+----------------+-----------------+
| userId | userName | count of item1 | count of item2 | count of item3 |
+--------+----------+----------------+----------------+-----------------+
| 1 | kim | 10 | 10 | 5 |
SELECT
userID,
userName,
SUM(CASE WHEN ItemID = 2 THEN 1 ELSE 0 END) AS count_of_item1,
SUM(CASE WHEN ItemID = 3 THEN 1 ELSE 0 END) AS count_of_item2,
SUM(CASE WHEN ItemID = 4 THEN 1 ELSE 0 END) AS count_of_item3
FROM
My_Table
GROUP BY
userID,
userName
This is called conditional aggregation. Use CASE for this.
With COUNT:
select
count(case when ItemID = 1 then 1 end) as count_item1,
count(case when ItemID = 2 then 1 end) as count_item2,
count(case when ItemID = 3 then 1 end) as count_item3
...
(then 1 could also be anything else except null, e.g. then 'count me'. This works because COUNT counts non-null values and when omitting the ELSE in CASE WHEN you get null. You could also explicitly add else null.)
Or with SUM:
select
sum(case when ItemID = 1 then 1 else 0 end) as count_item1,
sum(case when ItemID = 2 then 1 else 0 end) as count_item2,
sum(case when ItemID = 3 then 1 else 0 end) as count_item3
...
This is how you would do it :
select userId,
username,
SUM(CASE WHEN ItemID = '2' THEN 1 ELSE 0 END) AS Item2-Cnt,
SUM(CASE WHEN ItemID = '3' THEN 1 ELSE 0 END) AS Item3-Cnt,
SUM(CASE WHEN ItemID = '4' THEN 1 ELSE 0 END) AS Item4-Cnt
FROM userTable
GROUP BY userID, userName

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;

how to get value x without code duplication

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.

Querying Counts on Field Values in a Single Table

I have a single product table with multiple fields which contain user evaluations of various attributes:
product | attr_1_eval | attr_2_eval | attr_3_eval
ABC | Correct | Incorrect | Null
DEF | Incorrect | Null | Null
XYZ | Undetermined | Null | Incorrect
123 | Null | Undetermined | Correct
456 | Incorrect | Correct | Correct
I need to write a query which totals up those attribute evaluations across all products (where not null):
evaluation | correct | incorrect | undetermined
attr_1 | 1 | 2 | 1
attr_2 | 1 | 1 | 1
attr_3 | 2 | 1 | 0
This SQL gets me part way there:
SELECT
SUM(CASE WHEN attr_1_eval = 'Correct' then 1 else 0 END) AS correct,
SUM(CASE WHEN attr_1_eval = 'Incorrect' then 1 else 0 END) AS incorrect,
SUM(CASE WHEN attr_1_eval = 'Undetermined' then 1 else 0 END) AS undetermined,
SUM(CASE WHEN attr_2_eval = 'Correct' then 1 else 0 END) AS correct,
...
FROM product
But it doesn't group attr_1, attr_2.. by rows with error counts in the columns (as in the desired result set above). I'm using Postgres but help in any flavor of SQL would be most welcome.
Could you do 3 unions?
SELECT
'attr_1' AS evaluation,
SUM(CASE WHEN attr_1_eval = 'Correct' then 1 else 0 END) AS correct,
SUM(CASE WHEN attr_1_eval = 'Incorrect' then 1 else 0 END) AS incorrect,
SUM(CASE WHEN attr_1_eval = 'Undetermined' then 1 else 0 END) AS undetermined
FROM product
UNION
SELECT
'attr_2' AS evaluation,
SUM(CASE WHEN attr_2_eval = 'Correct' then 1 else 0 END) AS correct,
SUM(CASE WHEN attr_2_eval = 'Incorrect' then 1 else 0 END) AS incorrect,
SUM(CASE WHEN attr_2_eval = 'Undetermined' then 1 else 0 END) AS undetermined
FROM product
UNION
SELECT
'attr_3' AS evaluation,
SUM(CASE WHEN attr_3_eval = 'Correct' then 1 else 0 END) AS correct,
SUM(CASE WHEN attr_3_eval = 'Incorrect' then 1 else 0 END) AS incorrect,
SUM(CASE WHEN attr_3_eval = 'Undetermined' then 1 else 0 END) AS undetermined
FROM product
It's not the most elegant/efficient solution probably but it should get what you want
It's a little brute force, and I hate the fact that it scans the table three times, but this does appear to get the desired output. I'm sorry I don't know PostGres, but this should work in Oracle:
select
Attribute_name,
Sum (correct) as Correct,
sum (incorrect) as Incorrect,
sum (undetermined) as Undetermined
from
(
select
'attr_1' as Attribute_Name,
decode (attr_1_eval, 'Correct', 1, 0) as correct,
decode (attr_1_eval, 'Incorrect', 1, 0) as incorrect,
decode (attr_1_eval, 'Undetermined', 1, 0) as undetermined
from product
union all
select
'attr_2',
decode (attr_2_eval, 'Correct', 1, 0),
decode (attr_2_eval, 'Incorrect', 1, 0),
decode (attr_2_eval, 'Undetermined', 1, 0)
from product
union all
select
'attr_3',
decode (attr_3_eval, 'Correct', 1, 0),
decode (attr_3_eval, 'Incorrect', 1, 0),
decode (attr_3_eval, 'Undetermined', 1, 0)
from product
)
group by Attribute_Name