I have a table where each row has a description field as well as a boolean value. I'm trying to write a query where I can group by each respective description, and see the percentage of times that the boolean was true.
Example table:
PID Gender SeniorCitizen
1 M 1
2 M 1
3 F 0
4 F 1
5 M 0
And I want a query that will return this:
Gender SeniorPct
M .66
F .50
I've got to the point where I have a query that will calculate the individual percentages for a male or female - but I want to see both results at once
SELECT Gender, COUNT(*) * 1.0 /
(SELECT COUNT(*) FROM MyTable WHERE Gender='M')
FROM MyTable WHERE Gender='M' and SeniorCitizen=1;
I've been trying to include a "GROUP BY Gender" statement in my outer SELECT above, but I can't seem to figure out how to tweak the inner SELECT to get the correct results after tweaking the outer SELECT as such.
(I tested this under MySQL, please check if the same idea can be applied to the SQLite.)
To find the number of seniors (per gender), we can treat the bits as numbers and simply sum them up:
SELECT
Gender,
SUM(SeniorCitizen) Seniors
FROM MyTable
GROUP BY Gender
GENDER SENIORS
M 2
F 1
Based on that, we can easily calculate percentages:
SELECT
Gender,
SUM(SeniorCitizen) / COUNT(*) * 100 SeniorsPct
FROM MyTable
GROUP BY Gender
GENDER SENIORSPCT
M 66.6667
F 50
You can play with it in this SQL Fiddle.
UPDATE: Very similar idea works under SQLite as well. Please take a look at another SQL Fiddle.
Try the following:
CREATE TABLE #MyTable
(
PID INT,
Gender VARCHAR(1),
SeniorCitizen BIT
)
INSERT INTO #MyTable
(
PID,
Gender,
SeniorCitizen
)
SELECT 1, 'M', 1 UNION
SELECT 2, 'M', 1 UNION
SELECT 3, 'F', 0 UNION
SELECT 4, 'F', 1 UNION
SELECT 5, 'M', 0
SELECT
Gender,
COUNT(CASE WHEN SeniorCitizen = 1 THEN 1 END), -- Count of SeniorCitizens grouped by Gender
COUNT(1), -- Count of all users grouped by Gender
CONVERT(DECIMAL(2, 2), -- You can ignore this if you want
COUNT(CASE WHEN SeniorCitizen = 1 THEN 1 END) * 1.0 / COUNT(1) -- Your ratio
)
FROM
#MyTable
GROUP BY
Gender
Related
Is it possible to get only the countrys who just played in the pre round
Country Round
Germany Pre Round
Germany Quater final
Spain Pre Round
Portugal Pre Round
And I just want to get the countrys which only played in the pre round. So the result should look like this:
Country
Spain
Portugal
You can group by country and set the conditions in the having clause:
select country
from tablename
group by country
having count(*) = 1 and max(round) = 'Pre Round'
You can try the below using not exists
select country from c
where not exists
(select 1 from c as c1 where c.country=c1.country and roundval<>'Pre Round')
Two more for fun. The first is kind of a variation on #forpas', assigning a numeric value to each round, representing the progression through the rounds, and then getting the highest for the country (which would be simpler if the rounds were stored separately with a round number):
select country
from your_table
group by country
having max(case round
when 'Pre Round' then 1
when 'Quater final' then 2
when 'Semi final' then 3
when 'Final' then 4
end) = 1;
If you wanted to find countries that were in the quarters but not semis then you just need to change to = 2, etc.
The second is overkill here, but could be useful to look for more complicated combinations in other types of data:
select country
from your_table
pivot (
count(*) for round in (
'Pre Round' as pre, 'Quater final' as quarter, 'Semi final' as semi, 'Final' as final
)
)
where pre = 1 and quarter = 0 and semi = 0 and final = 0;
Obviously in your example you wouldn't ever have quarter as 0 and then either semi or final as 1 - you can't get to those rounds without playing the quarters; but for other data you might want a mix.
You could use a inner join on subquery for country wih round 're Round' and check for distinct count
select m.Contry
from my_table m
inner join (
select Country
from my_table
where round ='Pre Round'
) t on t.country = m.country
group by m.Country
having count(distinct m.round ) = 1
I have the below table as an output of a SQL query
ID Car Type Units Sold
---------------------------
1 Sedan 250
2 SUV 125
3 Total 375
I want a SQL query / procedure to produce below output
ID Car Type Units Sold
--------------------------
1 Sedan 250
2 SUV 125
3 Total 375
4 Sedan_Pct 66.67 (250/375)
5 SUV_Pct 33.33 (125/375)
Please note that Car Type will be increased in future and I want the percentage of each car type which should be appended to current table as '_Pct'.
Typically we might expect to see the percentages as a separate column, not as separate rows. That being said, we can generate the output you want using grouping sets in SQL Server:
WITH cte AS (
SELECT ID, CarType, SUM (UnitsSold) AS UnitsSold
FROM yourTable
GROUP BY
GROUPING SETS((ID, CarType), (CarType), ())
)
SELECT
ID,
COALECSE(CarType, 'Total') AS CarType,
CASE WHEN ID IS NOT NULL OR CarType IS NULL
THEN UnitsSold
ELSE 100.0 * UnitsSold /
SUM(CASE WHEN ID IS NOT NULL THEN UnitsSold END) OVER () END AS PctUnitsSold
FROM cte
ORDER BY
ID DESC,
CASE WHEN CarType IS NULL THEN 0 ELSE 0 END,
CarType;
Demo
A simpler solution will be using Union
SELECT CarType, UnitSold FROM Car
UNION
SELECT 'Total' CarType, SUM(UnitSold) UnitSold FROM Car
UNION
SELECT CarType + '_Pct' AS CarType, UnitSold / (SELECT SUM(UnitSold) FROM Car) * 100 AS UnitSold FROM Car
Might not be ideal in the long run
query for mysql server
use union all in view or stored procedure -
declare #totalunitsold numeric(15,0);
set #totalunitsold = (select unitsold from car where cartype='total')
select cartype,unitsold from car
union all
select cartype + '_pct', (unitsold/#totalunitsold) as pct from car
this may help you
SELECT CarType+'_Pct', UnitSold/TotalSale*100
FROM Car cross join (select sum(UnitSold) TotalSale from Car) X
you can union that with your Table
Don't do it! Just add an additional column, not new rows:
select t.*,
t.units_sold * 100.0 / sum(case when t.car_type = 'Total' then units_sold end) over () as ratio
from (<your query here>) t;
One fundamental reason why you want a different column is because ratio has a different type from units_sold. Everything in a column (even in a result set) should be a similar attribute.
What is the difference between the two SQL queries below other than Query2 returning an additional field? Are there any possible scenarios where the output of the two queries would be different (other than the additional field in Query2)
Query1:
SELECT Field1, COUNT(*)
FROM Table1
GROUP BY Field1
HAVING COUNT(*) > 1
Query2:
SELECT Field1, Field2, COUNT(*)
FROM Table1
GROUP BY Field1, Field2
HAVING COUNT(*) > 1
Absolutely, these are different. Query2's Group By clause specifies an extra field. That means when the results are aggregated, they will be aggregated for the combined unique values of Field1 AND Field2. That is, two records are aggregated if and only if both Field1 and Field2 are equal.
For example:
SELECT Profession, Count(*)
FROM People
GROUP BY Profession
HAVING Count(*) > 1
will return a list of professions with associated counts like:
Software Developer, 10
PM, 5
Tester, 2
whereas:
SELECT Profession, Gender, Count(*)
FROM People
GROUP BY Profession, Gender
HAVING Count(*) > 1
will return a list of professions broken out by gender like:
Software Developer, Male, 5
Sofware Developer, Female, 5
PM, Male, 3
PM, Female, 2
Tester, Male, 2
Edit with additional requested information:
You can retrieve counts of professions with rows for both genders via:
SELECT Profession, Count(*)
FROM People
GROUP BY Profession
HAVING SUM(case Gender when 'Female' then 1 else 0 end) > 0 AND SUM(case Gender when 'Male' then 1 else 0 end) > 0
It gets a bit hairy (need subqueries) if you also need associated gender counts
Extra group by clause in query 2 filters records.To know more look at below example.
test data:
id name
1 a
2 b
3 a
4 a
So when I say group by name,sql first filters out distinct records for name which goes like below for the below query
select name,sum(id)
from test
group by name
--first filter out distinct values for group by column (here name)
a
b
--next for each distinct record ,how many values fall into that category..
a 1 a
4 a
3 a
b 2 b
So from the above groups ,now you can calculate any aggregations on the group in our case,it is sum,so next output will go some thing like this
a 8
b 2
As you can see from above output,you also can calculate,any aggregation on group (here a and b values) ,like give me count(id),len(name) on group like below
select name,len(name),sum(id)
from test
group by name
The same thing happens when you group by another field,lets say like below
select id,name
from
test
group by id,name
so in above case,sql first filters alldistinct records for id,name
1 a
2 b
3 a
4 a
next step is to get records which fall for each group
groupby columns --columns which fall into this
1 a 1 a
2 b 2 b
3 a 3 a
4 a 4 a
Now you can calculate aggergations on above groups.hope this helps in visualizing your group by.further having will eliminate groups after group by phase,where will eliminate record before group by phase
I want to do the following:
1) Find the total rows in a table
2) Find the total rows that meets a certain criteria.
3) Subtract (1) from (2).
Sample table Employees:
EmployeeID Nationality
1 Brazil
2 Korea
3 Germany
4 Brazil
5 Brazil
What I've tried:
SELECT count(EmployeeID) as Total from Employees
UNION
SELECT count(EmployeeID) as Brazilians from Employees
WHERE Nationality = 'Brazil'
Result:
Total
5
3
Row 1 will give me the total Employees. Row 2 will give me the Brazilian Employees.
I used UNION to see if I could subtract row 2 from row 1.
I could do this using CASE and SUM(), but that would require the row_number() function, which I can't use given that I'm using WebSQL. Is there another way to index these rows to be able to subtract?
Is there another approach I could use to solve this seemingly simple problem?
How about counting the rows that don't meet that criteria?
SELECT COUNT(EmployeedID) as non_brazilians
FROM Employees
WHERE Nationality <> 'Brazil';
You can use conditional aggregation:
select count(*) as TotalRows,
sum(case when Nationality = 'Brazil' then 1 else 0 end) as Brazilians,
sum(case when Nationality <> 'Brazil' then 1 else 0 end) as nonBrazilians
from Employee;
This assumes that Nationality is never NULL. If that is possible, the last condition should be:
sum(case when Nationality = 'Brazil' then 0 else 1 end) as nonBrazilians
Try this:
SELECT count(*) AS TotalRows
, (SELECT count(EmployeeID) FROM WHERE Nationality = 'Brazil') as Brazilians
, (count(*) - (SELECT count(EmployeeID) FROM WHERE Nationality = 'Brazil')) AS Subtract1From2
FROM Employee
I am dealing with a poorly designed database column which has values like this
ID cid Score
1 1 3 out of 3
2 1 1 out of 5
3 2 3 out of 6
4 3 7 out of 10
I want the aggregate sum and percentage of Score column grouped on cid like this
cid sum percentage
1 4 out of 8 50
2 3 out of 6 50
3 7 out of 10 70
How do I do this?
You can try this way :
select
t.cid
, cast(sum(s.a) as varchar(5)) +
' out of ' +
cast(sum(s.b) as varchar(5)) as sum
, ((cast(sum(s.a) as decimal))/sum(s.b))*100 as percentage
from MyTable t
inner join
(select
id
, cast(substring(score,0,2) as Int) a
, cast(substring(score,charindex('out of', score)+7,len(score)) as int) b
from MyTable
) s on s.id = t.id
group by t.cid
[SQLFiddle Demo]
Redesign the table, but on-the-fly as a CTE. Here's a solution that's not as short as you could make it, but that takes advantage of the handy SQL Server function PARSENAME. You may need to tweak the percentage calculation if you want to truncate rather than round, or if you want it to be a decimal value, not an int.
In this or most any solution, you have to count on the column values for Score to be in the very specific format you show. If you have the slightest doubt, you should run some other checks so you don't miss or misinterpret anything.
with
P(ID, cid, Score2Parse) as (
select
ID,
cid,
replace(Score,space(1),'.')
from scores
),
S(ID,cid,pts,tot) as (
select
ID,
cid,
cast(parsename(Score2Parse,4) as int),
cast(parsename(Score2Parse,1) as int)
from P
)
select
cid, cast(round(100e0*sum(pts)/sum(tot),0) as int) as percentage
from S
group by cid;