Inserting results of two sums together to a table - sql

From my previous question, I've managed to get things to work using the Microsoft SQL Server and importing the excel file to a new database. Now, my question is dealing with writing the right sql commands. I'm trying to sum up the same column of a table twice using different criteria, and then input those numbers to a different table. I know how to insert a sum to a table, but I wonder how I can insert two sums that came from the same column simultaneously (since each time I insert, a new row is created) to the same row of a table.
Additional question: what is the way I should organize my results are dependent on values from a third table? Sample data as follows.
Some sample data:
DeptID Department
15 Eng
16 Eng
17 Mkt
18 Mkt
| Person | DeptID | Type | Number |
+--------+------------+------+---------+
| A | 15 | p1 | 1 |
| B | 18 | p2 | 5 |
| C | 16 | p2 | 10 |
| D | 17 | p1 | 7 |
| E | 18 | p1 | 11 |
| F | 16 | p2 | 12 |
So the result I should give is as such:
| Department | Sum of p1 | Sum of p2 |
+------------+------------+-----------+
| Eng | 1 | 22 |
| Mkt | 18 | 5 |
What I've tried is as follows:
select sum(Amount) as engsump1 from Sheet1$
where Department = 'Eng' and Type = 'p1'
select sum(Amount) as engsump2 from Sheet1$
where Department = 'Eng' and Type = 'p2'
select sum(Amount) as mktsump1 from Sheet1$
where Department = 'Mkt' and Type = 'p1'
select sum(Amount) as mktsump2 from Sheet1$
where Department = 'Mkt' and Type = 'p2'
Run it once and after I see the results, perform the insert into function. I'm just wondering if I can do this two steps in one step.

To sum the 'number' column in each group, you can use the SUM() function; Just put a case statement inside there, so you can sum the cases when type is p1, and sum the cases when type is p2.
To separate the departments, just use a GROUP BY clause. Try this:
SELECT department,
SUM(CASE WHEN type = 'p1' THEN number ELSE 0 END) AS totalP1,
SUM(CASE WHEN type = 'p2' THEN number ELSE 0 END) AS totalP2
FROM myTable
GROUP BY department;
Here is an SQL Fiddle example.

Related

Calculating percentage by group using Teradata

I'm trying to create a table that displays the percentage of counts per state dependent on the indicator.
Here's an example of the dataset I'm using to create my new table.
+-------------+-------+-------+
| Indicator | State | Count |
+-------------+-------+-------+
| Registered | CA | 25 |
| Registered | FL | 12 |
| Total | CA | 50 |
| Total | FL | 36 |
+-------------+-------+-------+
I'm trying to create a new table that would have a Percentage for each corresponding row like this:
+-------------+-------+-------+------------+
| Indicator | State | Count | Percentage |
+-------------+-------+-------+------------+
| Registered | CA | 25 | 50 |
| Registered | FL | 12 | 33.3 |
| Total | CA | 50 | . |
| Total | FL | 36 | . |
+-------------+-------+-------+------------+
So far, i've tried doing the below query:
select indicator, state, count
, case when (select count from table where indicator='Registered') * 100 / (select count from table where indicator='Total')
when indicator = 'Total' then . end as Percentage
from table;
This doesn't work because I get an error: "Subquery evaluated more than one row." I'm guessing its because I'm not taking into account the state in the case when statement, but i'm not sure as to how I would go about that.
What would be the best way to do this?
Just join the table back with itself.
select a.indicator, a.state, a.count
, case when (indicator='Total') then null
else 100 * a.count/b.count
end as Percentage
from table a
inner join (select state,count from table where indicator='Total') b
on a.state = b.state
;
You can use window functions:
select t.*,
(case when indicator <> 'Total'
then count * 100.0 / sum(case when indicator = 'Total' then indicator end) over (partition by state)
end) as percentage
from t;

SQL - display from table groups of rows as columns

I am trying to take range of codes in one column and display their results under one column per category.
Example of my table:
ID | TEST_NUM | RESULT |
---+----------+--------+
1 | R1 | 33 |
1 | R2 | 31 |
1 | C1 | 24 |
1 | C2 | 19 |
by query from table above i am trying to get the next table:
ID | TEST_NUM OF R ONLY | R RESULTS | C RESULTS
---+--------------------+-----------+----------
1 | R1 | 33 | 24
1 | R2 | 31 | 19
String operations can vary by database. I will assume yours supports left() and right() (which are easily represented by similar functions).
select id, max(test_num) as test_num,
max(case when test_num like 'R%' then result end) as r_result,
max(case when test_num like 'C%' then result end) as c_result
from t
group by id, right(test_num, 1)

Combine two rows with just 2 different column values into one

I have a table that keeps records of Workplace Accidents. Each IncidentNo is supposed to be unique. The organization that provided me the data in such a way that each IncidentNo has two entries, one for each gender. Format of the table is something like this. (Table has around 110 columns, I just showed the ones related to the question. All of the columns are varchar)
+------------+--------+----------------+
| IncidentNo | Gender | PersonnelCount |
+------------+--------+----------------+
| 123456 | M | 150 |
| 123456 | F | 100 |
| 789012 | M | 31 |
| 789012 | F | 42 |
+------------+--------+----------------+
What I need is to combine these columns in such a way that table (Doesn't matter if it is on the same table or inserted into a new one) is something like this:
+------------+----------------------+--------------------+
| IncidentNo | FemalePersonnelCount | MalePersonnelCount |
+------------+----------------------+--------------------+
| 123456 | 100 | 150 |
| 789012 | 42 | 31 |
+------------+----------------------+--------------------+
I thought to use Left Outer Join to insert data to a new table but couldn't figure out how.
Just use conditional aggregation:
select incidentno, sum(PersonnelCount) as total_PersonnelCount,
sum(case when gender = 'M' then PersonnelCount else 0 end) as nummales,
sum(case when gender = 'F' then PersonnelCount else 0 end) as numfemales,
from t
group by incidentno;
I would calculate the total as well, just to be sure that the total matches the sum of 'M' and 'F'.
In its rawest form, you can self join:
select x.IncidentNo,
sum(a.personelcount) as Female,
sum(b.personelcount) as Male
from Accidents x
left join Accidents a
on a.incidentno = x.incidentno
and a.Gender = 'F'
left join Accidents b
on b.incidentno = x.incidentno
and b.Gender = 'M'
group by x.IncidentNo -- I left this out originally because I'm an idiot

How to get a MAX and a COUNT from a three table join?

I got an interview question where there's a Car sale modeled in a DB. Each Car represents a physical car in a Car sale which refers to a Make and a Model table. A Sale table keeps track of each Car that is sold. A Sale only consists of one Car, so there's a record in Sale per every unique Car that had been sold.
The question was to find-out the name of the most sold Model in the car sale. I answered with a 3-level nested query. The interviewer specifically asked for a solution using joins where I only succeeded in just joining the tables without the aggregates.
How would you join 3 tables as below (Car, Make, Sale) while using two other aggregates?
Here's a rough sketch of the schema. The most sold Model here should return 'Corolla'
Car
| carid| modid | etc...
_________________
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
Make
| mkid | name |
_________________
| 1 | Toyota |
| 2 | Nissan |
| 3 | Chevy |
| 4 | Merc |
| 5 | Ford |
Model
| modid| name | mkid |
________________________
| 1 | Corolla| 1
| 2 | Sunny | 2
| 3 | Carina | 1
| 4 | Skyline| 2
| 5 | Focus | 5
Sale
| sid | carid | etc...
_________________
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
Edit:
Using MS SQL Server 2008
Output needed:
Model Name | Count
_____________________
Corolla | 3
i.e. The model of the Car that has been sold the most.
Notice only 3 Corollas and 2 Sunnys are in the Car table while Sale table corresponds to each of those with other sales detail. The 5 Sale records are actually Corolla, Corolla, Corolla, Sunnnu and Sunny.
Since you are using SQL Server 2008, make use of Common Table Expression and Window Function.
WITH recordList
AS
(
SELECT c.name, COUNT(*) [Count],
DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) rn
FROM Sale a
INNER JOIN Car b
ON a.carid = b.carID
INNER JOIN Model c
ON b.modID = c.modID
GROUP BY c.Name
)
SELECT name, [Count]
FROM recordList
WHERE rn = 1
SQLFiddle Demo
When interviewers ask for this they usually want you to say that you'd use windowed functions. You could give each sale a unique ascending number partitioned by model and the highest sale number you'd get would be the max count.
http://www.postgresql.org/docs/9.1/static/tutorial-window.html
Following query works on oracle 11g . here's fiddle link
SELECT name FROM (
SELECT model.name AS name FROM car , sale , model
WHERE car.carid=sale.carid
AND car.modid=model.modid
GROUP BY model.name
ORDER BY count(*) DESC )
WHERE rownum = 1;
Or
SELECT name FROM (
SELECT model.name AS name FROM car natural join sale natural join model
GROUP BY model.name
ORDER BY count(*) DESC )
WHERE rownum = 1;
OUTPUT
| NAME |
-----------
| Corolla |
Based on your newly added SQL Server 2008 tag. If you are using a different RDBMS you'll probably need to use limit instead of top and place it at the end of the top_sold_car subquery.
select Make.name as Make, Model.name as Model
from (
select top 1 count(*) as num_sold
from Car
group by modid
order by num_sold desc) as top_sold_car
join Model
on (top_sold_car.modid = Model.modid)
join Make
on (Model.mkid = Make.mkid)

Conditionally Summing the same Column multiple times in a single select statement?

I have a single table that shows employee deployments, for various types of deployment, in a given location for each month:
ID | Location_ID | Date | NumEmployees | DeploymentType_ID
As an example, a few records might be:
1 | L1 | 12/2010 | 7 | 1 (=Permanent)
2 | L1 | 12/2010 | 2 | 2 (=Temp)
3 | L1 | 12/2010 | 1 | 3 (=Support)
4 | L1 | 01/2011 | 4 | 1
5 | L1 | 01/2011 | 2 | 2
6 | L1 | 01/2011 | 1 | 3
7 | L2 | 12/2010 | 6 | 1
8 | L2 | 01/2011 | 6 | 1
9 | L2 | 12/2010 | 3 | 2
What I need to do is sum the various types of people by date, such that the results look something like this:
Date | Total Perm | Total Temp | Total Supp
12/2010 | 13 | 5 | 1
01/2011 | 10 | 2 | 1
Currently, I've created a separate query for each deployment type that looks like this:
SELECT Date, SUM(NumEmployees) AS "Total Permanent"
FROM tblDeployment
WHERE DeploymentType_ID=1
GROUP BY Date;
We'll call that query qSumPermDeployments. Then, I'm using a couple of joins to combine the queries:
SELECT qSumPermDeployments.Date, qSumPermDeployments.["Total Permanent"] AS "Permanent"
qSumTempDeployments.["Total Temp"] AS "Temp"
qSumSupportDeployments.["Total Support"] AS Support
FROM (qSumPermDeployments LEFT JOIN qSumTempDeployments
ON qSumPermDeployments.Date = qSumTempDeployments.Date)
LEFT JOIN qSumSupportDeployments
ON qSumPermDeployments.Date = qSumSupportDeployments.Date;
Note that I'm currently constructing that final query under the assumption that a location will only have temp or support employees if they also have permanent employees. Thus, I can create the joins using the permanent employee results as the base table. Given all of the data I currently have, that assumption holds up, but ideally I'd like to move away from that assumption.
So finally, my question. Is there a way to simplify this down to a single query or is it best to separate it out into multiple queries - if for no other reason that readability.
SELECT Date,
SUM(case when DeploymentType_ID = 1 then NumEmployees else null end) AS "Total Permanent",
SUM(case when DeploymentType_ID = 2 then NumEmployees else null end) AS "Total Temp",
SUM(case when DeploymentType_ID = 3 then NumEmployees else null end) AS "Total Supp"
FROM tblDeployment
GROUP BY Date
Try this:
SELECT
Date,
SUM(CASE WHEN DeploymentType_ID=1 THEN NumEmployees ELSE 0 END) AS "Total Permanent",
SUM(CASE WHEN DeploymentType_ID=2 THEN NumEmployees ELSE 0 END) AS "Total Temporary",
SUM(CASE WHEN DeploymentType_ID=3 THEN NumEmployees ELSE 0 END) AS "Total Support"
FROM tblDeployment
GROUP BY Date;