How do I count and replace elements in an sql column - sql

I have a table with a column that contains numbers 1 - 5, it looks something like the following
Column
1
2
4
3
2
1
5
2
How do I count the number of times each number shows up so that my final table looks like this:
Number | Count
one | 2
two | 3
three | 1
four | 1
five | 1

Try this:
select
case
when number = 1 then 'one'
when number = 2 then 'two'
when number = 3 then 'three'
when number = 4 then 'four'
when number = 5 then 'five'
end number,
count
from
(select number,count(*) count
from yourtable
group by number) s
In this case you have only 5 values so case is feasible to do the replacement. However, good practice would be to use a table with the number to name mapping and then join it to your counting query. So, assuming you have table tblMap with 2 columns - number and name, you would do;
select name, count(*)
from
yourtable t
inner join tblMap m on t.number = m.number
group by t.number, name --group by number so that results are ordered by number

SELECT
CASE
WHEN columnx = 1 THEN 'one'
WHEN columnx = 2 THEN 'two'
WHEN columnx = 3 THEN 'three'
WHEN columnx = 4 THEN 'four'
WHEN columnx = 5 THEN 'five'
ELSE 'unexpected' END as Number
, COUNT(*) as count_of
FROM YourTable
GROUP BY
CASE
WHEN columnx = 1 THEN 'one'
WHEN columnx = 2 THEN 'two'
WHEN columnx = 3 THEN 'three'
WHEN columnx = 4 THEN 'four'
WHEN columnx = 5 THEN 'five'
ELSE 'unexpected' END

Which database you need this, if oracle then you can use below query provided your number are between the range of 1 and 5373484
-- works only for values between 1 and 5373484 (i.e julian date)
select to_char(to_date(num_column,'j'),'jsp'),cnt
from
( select num_column,count(1) cnt
From TableA
group by num_column
)

select
case
when [Column] = '1' then 'One'
when [Column] = '2' then 'Two'
when [Column] = '3' then 'Three'
when [Column] = '4' then 'Four'
when [Column] = '5' then 'Five'
end [Number]
, COUNT (*) as [Count] from Table_Name
group by [Column]

Related

How can I partially transpose a table?

I have a table like this:
id
source
score
1
a
10
1
b
15
2
a
20
2
c
25
In this table, id and source make up a unique key. source can be one of 'a', 'b' or 'c'.
I want to transform it into this:
id
score_a
score_b
score_c
1
10
15
0
2
20
0
25
I don't mind if the 0s are nulls instead, if that would make it easier.
The target table should be the result of the following steps:
for every row in the table:
if source = 'a', add or update a row with id and score_a = score
if source = 'b', add or update a row with id and score_b = score
if source = 'c', add or update a row with id and score_c = score
I tried the following statement:
select id, score as score_a, null as score_b, null as score_c from tbl where source = 'a'
union all
select id, null as score_a, score as score_b, null as score_c from tbl where source = 'b'
union all
select id, null as score_a, null as score_b, score as score_c from tbl where source = 'c'
But it gave me this result instead:
id
score_a
score_b
score_c
1
10
null
null
2
20
null
null
1
null
15
null
2
null
null
25
How can I get a table like the one I want (with distinct id)?
You just need to use CASE to implement your logic and then flatten the result with some aggregation (I used MAX())
SELECT id,
MAX(CASE WHEN source='a' THEN score END) as score_a,
MAX(CASE WHEN source='b' THEN score END) as score_b,
MAX(CASE WHEN source='c' THEN score END) as score_c
FROM Tbl
GROUP BY id
DEMO
You indicated that null was OK, but if you have to have zeroes instead add COALESCE()

Calculation filtered sets in SQL

I have the following table invoving sort of 3 sets and I'm going to calculate the count of sets in whcich there is no (TaskId = 4), How can I achieve that?
SetId TaskId
1 0
1 1
1 4
2 0
2 2
2 3
3 0
3 2
3 4
Use conditional aggregation:
SELECT SetId
FROM yourTable
GROUP BY SetId
HAVING SUM(CASE WHEN TaskId = 4 THEN 1 ELSE 0 END) = 0;
The basic idea here is to scan each SetId group of records and count the number of times which a TaskId value of 4 occurs. The HAVING clause retains only groups for which the 4 value never occurs.
Use a CASE expression to check whether the TaskId value is 4. And use SUM function with grouping SetId.
Query
select [SetId],
SUM(case [TaskId] when 4 then 0 else 1 end) as [sum]
from [your_table_name]
group by [SetId];
I think you are looking for something like
SELECT *
FROM mytable t1
WHERE t1.SetId NOT IN (SELECT t2.SetId FROM mytable t2 WHERE t2.TaskId = 4)
(select the full sets that have no TaskId=4)
or
SELECT distinct SetId
FROM mytable t1
WHERE t1.SetId NOT IN (SELECT t2.SetId FROM mytable t2 WHERE t2.TaskId = 4)
(select just the SetIds that have no TaskId=4)

Merge multiple columns into one column with multiple rows

In PostgreSQL, how can I merge multiple columns into one column with multiple rows?
The columns are all boolean, so I want to:
Filter for true values only
Replace the true value (1) with the name of the column (A, B or C)
I have this table:
ID | A | B | C
1 0 1 0
2 1 1 0
3 0 0 1
4 1 0 1
5 1 0 0
6 0 1 1
I want to get this table:
ID | Letter
1 B
2 A
2 B
3 C
4 A
4 C
5 A
6 B
6 C
I think you need something like this:
SELECT ID, 'A' as Letter FROM table WHERE A=1
UNION ALL
SELECT ID, 'B' as Letter FROM table WHERE B=1
UNION ALL
SELECT ID, 'C'as Letter FROM table WHERE C=1
ORDER BY ID, Letter
SELECT ID,
(CASE
WHEN TABLE.A = 1 then 'A'
WHEN TABLE.B = 1 then 'B'
WHEN TABLE.C = 1 then 'C'
ELSE NULL END) AS LETTER
from TABLE
You may try this.
insert into t2 select id, 'A' from t1 where A=1;
insert into t2 select id, 'B' from t2 where B=1;
insert into t2 select id, 'C' from t3 where C=1;
If you care about the order, then you can do this.
insert into t3 select id, letter from t2 order by id, letter;
W/o UNION
You can use a single query to get the desired output.Real time example
select id
,regexp_split_to_table((
concat_ws(',', case
when a = 0
then null
else 'a'
end, case
when b = 0
then null
else 'b'
end, case
when c = 0
then null
else 'c'
end)
), ',') l
from c1;
regexp_split_to_table() & concat_ws()

To retrieve records having only two specific values

Have the following Data in the table
Example Table
ID Value
1 a
1 b
1 c
2 a
2 b
2 c
3 a
3 b
I need to retrieve records having ID with only two values a and b.
So i am expecting only the Record with ID 3 .
Can anyone help me with the query
I guess you could do something like
select
ID,
sum(case when value = 'a' then 1
when value = 'b' then 1
else 3 end)
from
table1
group by id
having
sum (case when value = 'a' then 1
when value = 'b' then 1
else 3 end) =2
SQL Fiddle
That will work:
select x.id from
(
select id from mytable where value = 'a'
union all
select id from mytable where value = 'b'
) x
group by x.id
having COUNT(*) = 2
and not exists (select * from mytable t where t.id = x.id and value <> 'a' and value <> 'b')

Exclude value of a record in a group if another is present

In the example table below, I'm trying to figure out a way to sum amount over id for all marks where mark 'C' doesn't exist within an id. When mark 'C' does exist in an id, I want the sum of amounts over that id, excluding the amount against mark 'A'. As illustration, my desired output is at the bottom. I've considered using partitions and the EXISTS command, but I'm having trouble conceptualizing the solution. If any of you could take a look and point me in the right direction, it would be greatly appreciated :)
sample table:
id mark amount
------------------
1 A 1
2 A 3
2 B 2
3 A 2
4 A 1
4 B 3
5 A 1
5 C 3
6 A 2
6 C 2
desired output:
id sum(amount)
-----------------
1 1
2 5
3 2
4 4
5 3
6 2
select
id,
case
when count(case mark when 'C' then 1 else null end) = 0
then
sum(amount)
else
sum(case when mark <> 'A' then amount else 0 end)
end
from sampletable
group by id
Here is my effort:
select id, sum(amount) from table t where not t.id = 'A' group by id
having id in (select id from table t where mark = 'C')
union
select id, sum(amount) from table t where t.id group by id
having id not in (select id from table t where mark = 'C')
SELECT
id,
sum(amount) AS sum_amount
FROM atable t
WHERE mark <> 'A'
OR NOT EXISTS (
SELECT *
FROM atable
WHERE id = t.id
AND mark = 'C'
)
GROUP BY
id
;