I am trying to calculate the % contribution of the a's in the "qual" column to the total sales for each loc.
loc | qual | sales
- - - - - - - - - - - -
us | a | 1,000
us | b | 500
gc | a | 200
gc | b | 400
So the answer that I would be looking for is US = 66.66% (1,000/1,500) and gc = 33.33% (200/600). So the return result would be....
loc | Pct
us | 66.66%
gc | 33.33%
Thanks!
You can do this with aggregation and window functions:
select loc, count(*) as sales, sum(count(*)) over () as total_sales,
count(*) / sum(1.0*count(*)) over () as sales_proportion
from t
group by loc;
If sales is really an integer, may should convert it to a floating point or decimal representation (you can just multiple by 1.0).
EDIT:
Oops, the above does something useful, but not what the OP asks for. Here is the simplest method:
select loc,
avg(case when qual = 'a' then 1.0 else 0 end) as proportion_a
from t
group by loc;
You need a conditional aggregate:
select loc,
100.00
* sum(case when qual = 'a' then sales else 0 end) -- only 'a' sales
/ sum(sales) as Pct -- all sales
from tab
group by loc
Change the precision of 100.00 * according to your needs.
Caution, unless the datatype of sales is a FLOAT or NUMBER you must multiply 100 * first and then divide.
To calculate the percentage, you know that you will need to sum over loc. But you don't want to group by loc and aggregate, because that will destroy parts of the table that you want to keep, and inner joining that resulting table back to the table you have here ... well, there's gotta be an easier way. And that's where a correlated subquery comes in! You can perform a subselect that counts the sales correlated to the loc in your table. The following code should give you what you want.
select loc,
sales * 100 / (
select sum(sales)
from my_table sq
where sq.loc = t.loc
) as pct
from my_table t
where qual = 'a'
Hope this helps!
Related
Does any one know how to create the following crosstab in Postgres?
For example I have the following table:
Store Month Sales
A Mar-2020 100
A Feb-2020 200
B Mar-2020 400
B Feb-2020 500
A Jan-2020 400
C Apr-2020 600
I would like the query to return the following crosstab, the column headings should not be hardcoded values but reflect the values in "month" column from the first table:
Store Jan-2020 Feb-2020 Mar-2020 Apr-2020
A 400 200 100 -
B - 500 400 -
C - - - 600
Is this possible?
Postgres does have a crosstab function, but I think using the built in filtering functionality is simple in this case:
select store,
sum(sales) filter (where month = 'Jan-2020') as Jan_2020,
sum(sales) filter (where month = 'Feb-2020') as Feb_2020,
sum(sales) filter (where month = 'Mar-2020') as Mar_2020,
sum(sales) filter (where month = 'Apr-2020') as Apr_2020
from t
group by store
order by store;
Note: This puts NULL values in the columns with no corresponding value, rather than -. If you really wanted a hyphen, you would need to convert the value to a string -- and that seems needlessly complicated.
Try this with CASE expression inside SUM(), here is the db-fiddle.
select
store,
sum(case when month = 'Jan-2020' then sales end) as "Jan-2020",
sum(case when month = 'Feb-2020' then sales end) as "Feb-2020",
sum(case when month = 'Mar-2020' then sales end) as "Mar-2020",
sum(case when month = 'Apr-2020' then sales end) as "Apr-2020"
from myTable
group by
store
order by
store
Output:
+---------------------------------------------------+
|store Jan-2020 Feb-2020 Mar-2020 Apr-2020|
+---------------------------------------------------+
| A 400 200 100 null |
| B null 500 400 null |
| C null null null 600 |
+---------------------------------------------------+
If you want to replace null values with 0 in the output then use coalesce()
e.g.
coalesce(sum(case when month = 'Jan-2020' then sales end), 0)
I am trying to write a query to get sums of payments from accounts for a month. I have been able to get it for the most part but I have hit a road block. My challenge is that I need a count of the amount of payments that are either < 10000 or => 10000. The business rules are that a single payment may not exceed 10000 but there can be multiple payments made that can total more than 10000. As a simple mock database it might look like
ID | AccountNo | Payment
1 | 1 | 5000
2 | 1 | 6000
3 | 2 | 5000
4 | 3 | 9000
5 | 3 | 5000
So the results I would expect would be something like
NumberOfPaymentsBelow10K | NumberOfPayments10K+
1 | 2
I would like to avoid doing a function or stored procedure and would prefer a sub query.
Any help with this query would be greatly appreciated!
I suggest avoiding sub-queries as much as possible because it hits the performance, specially if you have a huge amount of data, so, you can use something like Common Table Expression instead. You can do the same by using:
;WITH CTE
AS
(
SELECT AccountNo, SUM(Payment) AS TotalPayment
FROM Payments
GROUP BY AccountNo
)
SELECT
SUM(CASE WHEN TotalPayment < 10000 THEN 1 ELSE 0 END) AS 'NumberOfPaymentsBelow10K',
SUM(CASE WHEN TotalPayment >= 10000 THEN 1 ELSE 0 END) AS 'NumberOfPayments10K+'
FROM CTE
You can get the totals per account using SUM and GROUP BY...
SELECT AccountNo, SUM(Payment) AS TotPay
FROM payments
GROUP BY AccountNo
You can use that result to count the number over 10000
SELECT COUNT(*)
FROM (
SELECT AccountNo, SUM(Payment) AS TotPay
FROM payments
GROUP BY AccountNo
)
WHERE TotPay>10000
You can get the the number over and the number under in a single query if you want but that's a but more complicated:
SELECT
COUNT(CASE WHEN TotPay<=10000 THEN 1 END) AS Below10K,
COUNT(CASE WHEN TotPay> 10000 THEN 1 END) AS Above10K
FROM (
SELECT AccountNo, SUM(Payment) AS TotPay
FROM payments
GROUP BY AccountNo
)
i am relatively new to SQL. Each employee access an account for testing with a tech, sometimes it's a good attempt, sometimes it's bad, so I need to calculate the percentage of the bad attempts mostly, my report should look something like this:
SELECT
employee, event, total, percentage
FROM my_table
employee | event | total | percentage|
user1 | good | 50 | 50% |
user1 | bad | 50 | 50% |
Calculate the total in a subquery and then JOIN to calculate percentage on each row.
SELECT employee, event, COUNT(*), COUNT(*) * 100.0 / t.total as percentage
FROM my_table
JOIN (SELECT employee, count(*) total
FROM my_table
GROUP BY employee) T
ON my_table.employee = t.employee
GROUP BY employee, event
Try something like this calculate the bad event percentage for each employee
select employee,(sum(case when event = 'bad' then 1 else 0 end) / count(*)) * 100
From Yourtable
Group by employee
I have a table that consists of a customer ID, and the number of hours it took to place an order since they first registered.
An example would be:
UserId | TimeToPay
---------------------------------------------------
2DD6ABBB-C9A4-4373-B188-312DB8222859 | 0
C7438620-6431-4C13-B335-AA1A3E314C58 | 55
6AG22103-62B0-47A0-BE3F-7AE1A7A4C3B7 | 30
300A2E02-0799-47BB-BF36-070706F98149 | 8
43382839-E897-4E5F-A955-C9DDAF9B424B | 0
In the above example, 2 customers have placed an order within an hour of ordering something, and after 55 hours, all customers have placed an order. This table does not contain customers that have not placed an order yet. I am trying to create a query that shows cumulative percentages of how many customers have placed an order in what timespan. So my prefered output would be:
Hours | PercentageOfCustomers
-------------------------------
0 | 40
8 | 60
30 | 80
55 | 100
However, when I use answers like this or this one, I don't get cumulative percentages. How do I get my desired output?
You can use a windowed COUNT(*) to get a rolling total, and divide that by the number of total customers:
Select Distinct TimeToPay As Hours,
((Count(*) Over (Order By TimeToPay Asc) * 1.0) /
(Count(*) Over (Order By (Select Null)) * 1.0))
* 100 As PercentageOfCustomers
From Test
Order by Hours
Try This:
DECLARE #main_table TABLE ( UserId INT, TimeToPay INT)
INSERT INTO #main_table VALUES(1,0),(2,55),(3,30),(4,8),(5,0),(6,30),(7,30)
DECLARE #total INT = ( SELECT COUNT(col) FROM
( SELECT 'Z' col FROM #main_table GROUP BY TimeToPay )A GROUP BY col )
SELECT TimeToPay, (COUNT(TimeToPay)*100)/#total Percentage FROM #main_table
GROUP BY TimeToPay
Hope it helps. :)
This question is all for SQL Azure. I have a data set for various commodity prices by year and a unit price like:
Rice - 2007 - 0.5
Rice - 2007 - 0.3
Rice - 2007 - 0.8
Wheat - 2006 - 1.1
Wheat - 2006 - 1.4
etc
How can I create a pivot table that gives me the MAX and MIN price paid for each year for each commodity? I know how to do a pivot table that would give me something like the average - thats pretty easy. But I need my "main" pivot column to be the year and then each year would have its 2 "sub columns" for a MIN and MAX price and I'm not quite sure how to do that. Help!
Unless I am missing something in your explanation, you can do this easily without the PIVOT function:
select product,
year,
min(price) MinPrice,
max(price) MaxPrice
from yourtable
group by product, year
See SQL Fiddle with Demo.
If you want the data in separate columns, then there are a few ways that you can do this.
Aggregate function with CASE:
select product,
min(case when year=2006 then price else 0 end) [2006_MinPrice],
max(case when year=2006 then price else 0 end) [2006_MaxPrice],
min(case when year=2007 then price else 0 end) [2007_MinPrice],
max(case when year=2007 then price else 0 end) [2007_MaxPrice]
from yourtable
group by product
See SQL Fiddle with Demo
UNPIVOT and PIVOT:
The UNPIVOT is used to transform your column data into rows. Once in the rows, you can create the new columns with the year and then pivot:
select *
from
(
select product,
cast(year as varchar(4))+'_'+col as piv_col,
value
from
(
select product,
year,
min(price) MinPrice,
max(price) MaxPrice
from yourtable
group by product, year
) x
unpivot
(
value for col in (minPrice, maxPrice)
) u
) d
pivot
(
max(value)
for piv_col in ([2006_MinPrice], [2006_MaxPrice],
[2007_MinPrice], [2007_MaxPrice])
) piv;
See SQL Fiddle with Demo. These give the result:
| PRODUCT | 2006_MINPRICE | 2006_MAXPRICE | 2007_MINPRICE | 2007_MAXPRICE |
---------------------------------------------------------------------------
| Rice | 0 | 0 | 0.3 | 0.8 |
| Wheat | 1.1 | 1.4 | 0 | 0 |
If you have an unknown number of years, then you coul also implement dynamic sql.