How to sum and count in grouping in sql - sql

I have tables like below.
I would like to grouping and counting by referreing to its score.
customer score
A 10
A 20
B 30
B 40
C 50
C 60
First, I would like to sum score by customer
It achived by group by method.
customer score
A 30
B 70
C 110
second, I would like to count by binning following band.
I couldn't figure out how to count after grouping
band count
0-50 1
50-100 1
100- 0
Are there any way to achieve this?
Thanks

You could use a union approach:
WITH cte AS (
SELECT COUNT(*) AS score
FROM yourTable
GROUP BY customer
)
SELECT '0-50' AS band, COUNT(*) AS count, 0 AS position FROM cte WHERE score <= 50 UNION ALL
SELECT '50-100', COUNT(*), 1 FROM cte WHERE score > 50 AND score <= 100 UNION ALL
SELECT '100-', COUNT(*), 2 FROM cte WHERE score > 100
ORDER BY position;

Try the following with case expression
select
case
when score >= 0 and score <= 50 then '0-50'
when score >= 50 and score <= 100 then '50-100'
when score >= 100 then '100-'
end as band,
count(*) as count
from
(
select
customer,
sum(score) as score
from myTable
group by
customer
) val
group by
case
when score >= 0 and score <= 50 then '0-50'
when score >= 50 and score <= 100 then '50-100'
when score >= 100 then '100-'
end

I would recommend two levels of aggregation but with a left join:
select b.band, count(c.customer)
from (select 0 as lo, 50 as hi, '0-50' as band from dual union all
select 50 as lo, 100 as hi, '50-100' as band from dual union all
select 100 as lo, null as hi, '100+' as band from dual
) b left join
(select customer, sum(score) as score
from myTable
group by customer
) c
on c.score >= b.lo and
(c.score < b.hi or b.hi is null)
group by b.band;
This structure also suggests that the bands can be stored in a separate reference table. That can be quite handy, if you intend to reuse these across different queries or over time.

Related

What percentage of clients live between 1 & 3 miles

Tried code:
SELECT count(*) Count_people_inside_3miles
FROM CLIENT_DATA
WHERE (ABS(C_ADD_X) >=1 AND ABS(C_ADD_X) <=3) AND (ABS(C_ADD_Y) >=1 AND ABS(C_ADD_Y) <=3);
SELECT count(CLIENT_ID) Count_Total_people
FROM CLIENT_DATA;
Result:
COUNT_PEOPLE_INSIDE_3MILES
15
COUNT_TOTAL_PEOPLE
24
How do I calculate 15/24*100?
Use conditional aggregation to do it all in a single table scan:
SELECT COUNT(
CASE
WHEN ABS(C_ADD_X) BETWEEN 1 AND 3 AND ABS(C_ADD_Y) BETWEEN 1 AND 3
THEN 1
END
) / COUNT(*) * 100 AS percentage_people_inside_3miles
FROM CLIENT_DATA;

Divide each value of a column by the total count of records in a table

A query that is capable of dividing each value of a column by the total number of records in the table
I tried the following query
select ( (p.rank/count(*)) * 100 ) as rankratio from RankTable p;
I see an error and not able to execute the query.
for example
total records is 5 so (1/5)*100 = 20
RankTable
rank rankratio
1 20
2 40
3 60
4 80
5 100
use analytic count(*) over():
select ( (s.rank/s.total_count) * 100 ) as rankratio
from
(
select rank, count(*) over() as total_count
from RankTable p
)s
order by s.rank;

Oracle random segments select

I would like to select the following segment.
Random 5500 rows including the following segments:
Subcategorie (sex): - 3300 men
- 2200 women
Subcategorie (age): - 2140 between 18-34 years
- 2100 between 35-54 years
- 1260 between 55-99 years
How could I solve this in a select statement?
The problem is, you use the word "random" but you have a very precise break down of cohorts by age and sex. A truly random single query won't produce such exact quotas. So your query must necessarily be complicated: you need to divide the whole table into subsets which meet your constraints then randomly select from those subsets. Something like this...
select * from (
select * from whatever
where sex = 'M'
and age between 18 and 34
order by dbms_random.value
)
where rownum <= 1284
union all
select * from (
select * from whatever
where sex = 'M'
and age between 35 and 54
order by dbms_random.value
)
where rownum <= 1260
union all select * from (
select * from whatever
where sex = 'M'
and age between 55 and 99
order by dbms_random.value
)
where rownum <= 756
union all
select * from (
select * from whatever
where sex = 'F'
and age between 18 and 34
order by dbms_random.value
)
where rownum <= 856
union all
select * from (
select * from whatever
where sex = 'F'
and age between 35 and 54
order by dbms_random.value
)
where rownum <= 840
union all select * from (
select * from whatever
where sex = 'F'
and age between 55 and 99
order by dbms_random.value
)
where rownum <= 504
This may perform poorly, depending on the usual factors - size of table, indexing, etc - but it will produce those exact cohorts.
In case it's not obvious, the rownum bounds are the number of hits in each age group multiplied by the ratio of men to women (3:2).

How can I select top 3 for each group based on another column in sqlite?

I'm trying to get top 3 most profitable UserIDs in each country in one table using sqlite. I'm not sure where to use LIMIT 3.
Here is the table I have:
Country | UserID | Profit
US 1 100
US 12 98
US 13 10
US 5 8
US 2 5
IR 9 95
IR 3 90
IR 8 70
IR 4 56
IR 15 40
the result should look like this:
Country | UserID | Profit
US 1 100
US 12 98
US 13 10
IR 9 95
IR 3 90
IR 8 70
One pretty simple method is:
select t.*
from t
where t.profit >= (select t2.profit
from t t2
where t2.country = t.country
order by t2.profit desc
limit 1 offset 2
);
This assumes at least three records for each country. You can get around that with coalesce():
select t.*
from t
where t.profit >= coalesce((select t2.profit
from t t2
where t2.country = t.country
order by t2.profit desc
limit 1 offset 2
), t.profit
);
Since SQLite doesn't support windows function, so you can write a subquery be a seqnum by Country, then get top 3
You can try this query.
select t.Country,t.UserID,t.Profit
from(
select t.*,
(select count(*)
from T t2
where t2.Country = t.Country and t2.Profit >= t.Profit
) as seqnum
from T t
)t
where t.seqnum <=3
sqlfiddle:https://www.db-fiddle.com/f/tmNhRLGG2oKqCKXJEDsjfe/0
LIMIT won't be usefull as it applies to a whole result set.
I would create an auxiliary column "CountryRank" like this:
SELECT *, (SELECT COUNT() FROM Data AS d WHERE d.Country=Data.Country AND d.Profit>Data.Country)+1 AS CountryRank
FROM Data;
And query on that result:
SELECT Country, UserID, Profit
FROM (
SELECT *, (SELECT COUNT() FROM Data AS d WHERE d.Country=Data.Country AND d.Profit>Data.Profit)+1 AS CountryRank FROM Data)
WHERE CountryRank<=3
ORDER BY Country, CountryRank;

Find all records that match a GROUP BY result HAVING count > 1 in SQLite

The GROUP BY and HAVING isn't the hard part. This query results the summary:
SELECT date, account, amount, COUNT(1) AS num
FROM "transactions"
GROUP BY date, account, amount
HAVING num > 1
Something like:
date account amount num
2011-02-07 580416690 -6.4 2
2011-07-19 -50.0 2
2011-08-29 2445588 -22.0 2
2011-12-16 265113334 -0.1 3
But I dont want the summary (4 records). I want all the relevant records (so 2 + 2 + 2 + 3 = 9 records). If the GROUP BY was on 1 column, that wouldn't be hard either, but with 3 columns...
How do I get the actual records with those values? 1 query must be possible. Do I need 3 subqueries?
One way to do it is to join back to transactions
SELECT *
FROM transactions t JOIN
(
SELECT date, account, amount
FROM transactions
GROUP BY date, account, amount
HAVING COUNT(*) > 1
) d
ON (t.date = d.date
AND t.account = d.account
AND t.amount = d.amount) OR
(t.date = d.date
AND t.account IS NULL AND d.account IS NULL
AND t.amount = d.amount)
Here is a SQLFiddle demo