Let's say I have a table like this:
ID Income Expenses
-------------------------
1 $1000 $200
1 $2000 $400
2 $500 $200
2 $100 $60
I'd like to add one row per ID that sums the ID's Income and Expenses. For example:
ID Income Expenses
-------------------------
1-SUM $3000 $600
1 $1000 $200
1 $2000 $400
2-SUM $600 $260
2 $500 $200
2 $100 $60
Is this possible with SQL? I read about OVER and PARTITION BY but that'll add a new column, not a new row. Might a UNION be the best way to go here?
Try this
create table yourtable(id int,income int,expenses int)
insert into yourtable values (1,100,200),(1,150,250),(2,200,300),(2,250,350)
with CTE
as
(select ID,sum(income) as income,sum(expenses) as expenses,1 as isSum
from yourtable
group by id
)
select * from (
select *,0 as isSum from yourtable
union all
select * from CTE ) as t
order by id,issum
output
Using GROUP BY:
SQLFiddleDemo
CREATE TABLE tab(id INT , income INT , expenses INT);
INSERT INTO tab(id, income, expenses)
VALUES (1, 1000, 200),(1, 2000, 400),(2, 500, 200),(2, 100, 60)
SELECT id,
[income] = SUM(income),
[expenses] = SUM(expenses),
[grouping_level] = GROUPING_ID(id, income, expenses) /* 0 - actual data , 3 - group based on column income, expenses*/
FROM tab
GROUP BY GROUPING SETS ((id), (id,income, expenses))
Why grouping is useful:
Using GROUPING SETS gives great flexibility for instance when you want total sum use: GROUP BY GROUPING SETS ((id), (id,income, expenses), ())
GROUPING SETS can be combined with ROLLUP/CUBE.
GROUPING_ID indicates grouping level.
Number works like binary. GROUPING(a, b, c) if b i c is used for grouping you get 011B => 3
Related
Hi I have table like this in SQL anywhere database
CUSTID-------DDAT.----------AMOUNT
1. 01-01-2021. 1000
1. 02-02-2021. 2000
1. 03-02-2021. 3000
1. 04-02-2021. 4000
2. 01-04-2021. 1000
2. 02-04-2021. 2000
04-04-2021. 1000
I want data like this in VB.net where amount is only for one date and total amount is for 4 day
Cust id.------date ---------------Amount.-------Total amount
1. 04-04-2021. 4000. 10000
2. 04-04-2021. 1000. 4000
Can you give me any solution..thanks in advance
My take on it:
select custid, dat, amount, total_amount
from (
select custid, dat, amount, sum(amount) over (partition by custid) as total_amount
from data
) d
where dat = '2021-04-04' -- or any other date
Might be that the inner select is all that you need. Not sure if the filter on date is necessary.
I have a database table t with a sales table:
ID
TYPE
AGE
1
B
20
1
BP
20
1
BP
20
1
P
20
2
B
30
2
BP
30
2
BP
30
3
P
40
If a person buys a bundle it appears the bundle sale (TYPE B) and the different bundle products (TYPE BP), all with the same ID. So a bundle with 2 products appears 3 times (1x TYPE B and 2x TYPE BP) and has the same ID.
A person can also buy any other product in that single sale (TYPE P), which has also the same ID.
I need to calculate the average/min/max age of the customers but the multiple entries per sale tamper with the correct calculation.
The real average age is
(20 + 30 + 40) / 3 = 30
and not
(20+20+20+20 + 30+30+30 + 40) / 8 = 26,25
But I don't know how I can reduce the sales to a single row entry AND get the 4 needed values?
Do I need to GROUP BY twice (first by ID, then by AGE?) and if yes, how can I do it?
My code so far:
SELECT
AVERAGE(AGE)
, MIN(AGE)
, MAX(AGE)
, MEDIAN(AGE)
FROM t
but that does count every row.
Assuming the age is the same for all rows with the same ID (which in itself indicates a normalisation problem), you can use nest aggregation:
select avg(min(age)) from sales
group by id
AVG(MIN(AGE))
-------------
30
SQL Fiddle
The example in the documentation is very similar; and is explained as:
This calculation evaluates the inner aggregate (MAX(salary)) for each group defined by the GROUP BY clause (department_id), and aggregates the results again.
So for your version:
This calculation evaluates the inner aggregate (MIN(age)) for each group defined by the GROUP BY clause (id), and aggregates the results again.
It doesn't really matter whether the inner aggregate is min or max - again, assuming they are all the same - it's just to get a single value per ID, which can then be averaged.
You can do the same for the other values in your original query:
select
avg(min(age)) as avg_age,
min(min(age)) as min_age,
max(min(age)) as max_age,
median(min(age)) as med_age
from sales
group by id;
AVG_AGE MIN_AGE MAX_AGE MED_AGE
------- ------- ------- -------
30 20 40 30
Or if you prefer you could get the one-age-per-ID values once ina CTE or subquery and apply the second layer of aggregation to that:
select
avg(age) as avg_age,
min(age) as min_age,
max(age) as max_age,
median(age) as med_age
from (
select min(age) as age
from sales
group by id
);
which gets the same result.
SQL Fiddle
I need to make a table like below, which gives me a distribution of the number of orders made by a single customer ID
no_of_orders count_of_customer_id
1 537
2 845
3 193
4 80
... ...
How do I do this without manually printing out the no_of_orders (1, 2, 3, 4, 5, ...) in the query, As I expect there will be outliers which make hundreds of orders a day. I will be using BigQuery for this.
The daily_order table which I'm querying from has the columns order_payment (CASH, CREDIT, etc), service_type (A, B, C, etc), customer_id (unique value for each customer), and order_time (e.g. 2018-04-06 15:06:26 UTC). It looks like this:
order payment service_type customer_id order_time
CASH A 58128 2018-04-06 15:06:26 UTC
CREDIT B 58256 2018-04-06 15:08:34 UTC
First, use a select query to retrieve no_of_order for each customer by grouping customer_id and then from this query use another query to group each count.
select no_of_orders, count(*) as count_of_customer_id
from(
select count(*) as no_of_orders
from daily_order
group by customer_id
)
group by no_of_orders
order by no_of_orders;
I am trying to create a list of percentages from a dataset of transactional data using SAS/SQL to understand how a specific department contributes to overall sales count for a given quarter. For example, if there were 100 sales of Store ID 234980 and 20 of those were in department a in Q4 of 2006, then the list should output:
Store ID 234980 , 20%.
This is the code I am using to achieve this result.
data testdata;
set work.dataset;
format PostingDate yyq.;
run;
PROC SQL;
CREATE TABLE aggregatedata AS
SELECT DISTINCT testdata.ID,
SUM(CASE
WHEN testdata.Store='A' THEN 1 ELSE 0
END)/COUNT(Store) as PERCENT,
PostingDate
FROM work.testdata
group by testdata.ID, testdata.PostingDate;
QUIT;
However, the output I am receiving is more like this:
StoreID DepartmentA Quarter
100 1 2014Q1
100 0 2014Q2
100 1 2014Q2
100 0 2014Q2
100 0 2014Q2
100 0 2014Q2
101 1 2015Q3
101 0 2015Q3
101 0 2015Q4
Why does my code not aggregate to the store level?
If you want to group by QTR then you need to transform your date values into quarter values. Otherwise '01JAN2017'd and '01FEB2017'd would be seen as two distinct values even though they would both display the same using the YYQ. format.
proc sql;
create table aggregatedata as
select id
, intnx('qtr',postingdate,0,'b') as postingdate format=yyq.
, sum(store='A')/count(store) as percent
from work.testdata
group by 1,2
;
quit;
You do not want to set both DISTINCT and GROUP BY
Perhaps try:
select t.testingdate
,t.StoreID
,t.Department
,count(t.*) / count(select t2.*
from testdata t2
where t.testingdate = t2.testingdate
and t.StoreID = t2.StoreID) AS Percentage
from testdata t
group by t.testingdate
,t.StoreID
,t.Department
Alternately you could use a left join, which may be more efficient. The nested select to count all records, regardless of department may be more clear to read.
I'm having a hard time solving this, i hope someone can help me out with some tips or advice.
I have a Table in SQL with 3 columns:
Pencil Sales Notebook Sales Pen Sales
1 2 3
9 5 6
7 8 9
I made a query using "Union all" with the sum of each column.
My query looks like this:
select sum(pencilsales) from table1 union all
select sum(notebooksales) from table1 union all
select sum(pensales) from table1
and it gives me the following:
(No Column Name)
17
15
18
But i wanna know if there's a way of sorting this new query by using "desc" or something like that and to add a new column saying which one is each row, like this:
Sales Name
18 Pen Sales
17 Pencil Sales
15 Notebook Sales
Hope you can help me out with ideas and thank you in advance :)
select * from
(
select 'Pencil Sales' as Name, sum(pencilsales) as sales from table1
union all
select 'Notebook Sales', sum(notebooksales) from table1
union all
select 'Pen Sales', sum(pensales) from table1
) t order by sales desc
This is a pretty good candidate for UNPIVOT
SELECT
SUM(Sales) Sales,
[Name]
FROM Table1
UNPIVOT (
Sales
FOR [Name] IN ([pencilsales], [notebooksales], [pensales])
) up
GROUP BY [Name]
You can't unpivot in MySql. The sub-query gets what you want.
UNION is often not used in this case. SQL Server has the handy PIVOT/UNPIVOT to do this.
Also, your question is easier to understand if you have a key like "Sales Associate".
CREATE TABLE Sales
(
[Sales Assoicate Id] varchar(10),
[Pencil Sales] int,
[Notebook Sales] int,
[Pen Sales] int
);
INSERT INTO Sales ([Sales Associate Id],[Pencil Sales],[Notebook Sales],[Pen Sales]) VALUES ('Morgan',1,2,3);
INSERT INTO Sales ([Sales Associate Id],[Pencil Sales],[Notebook Sales],[Pen Sales]) VALUES ('Jane',9,5,6);
INSERT INTO Sales ([Sales Associate Id],[Pencil Sales],[Notebook Sales],[Pen Sales]) VALUES ('Jeff',7,8,9);
Looks like:
Sales Associate Id Pencil Sales Notebook Sales Pen Sales
Morgan 1 2 3
Jane 9 5 6
Jeff 7 8 9
To get the results how you asked for them use UNPIVOT!
SELECT SUM(Sales) Sales,
Name
FROM Sales
UNPIVOT
(Sales FOR Name IN ([Pencil Sales],[Notebook Sales],[Pen Sales])) SalesSummary
GROUP BY Name
Results:
Sales Name
15 Notebook Sales
18 Pen Sales
17 Pencil Sales