Consider a sales application where we have two tables SALES and INTERNAL_SALES.
Table "SALES" references the number of transactions made by each sales person outside the company.
Table "INTERNAL_SALES" references the number of transactions made by each sales person inside the company to another sales person.
SALES Table:
Each date has one entry against each sales person even if transactions are zero.
id | day | sales_person | number_of_transactions
1 | 2011-08-01 | Tom | 1000
2 | 2011-08-01 | Ben | 500
3 | 2011-08-01 | Anne | 1500
4 | 2011-08-02 | Tom | 0
5 | 2011-08-02 | Ben | 800
6 | 2011-08-02 | Anne | 900
7 | 2011-08-03 | Tom | 3000
8 | 2011-08-03 | Ben | 0
9 | 2011-08-03 | Anne | 40
INTERNAL_SALES Table:
This table logs only the transactions that were actually made between sales persons.
id | day | sales_person_from | sales_person_to | number_of_transactions
0 | 2011-08-01 | Tom | Ben | 10
1 | 2011-08-01 | Tom | Anne | 20
2 | 2011-08-01 | Ben | Tom | 50
3 | 2011-08-03 | Anne | Tom | 30
4 | 2011-08-03 | Anne | Tom | 30
Now the problem is to come up with total transactions by each sales person on a daily basis. The way I did this is:
SELECT day, sales_person, sum(num_transactions) from
(
SELECT day, sales_person, number_of_transactions As num_transactions FROM sales;
UNION
SELECT day, sales_person_from As sales_person, sum(number_of_transactions) As num_transactions FROM internal_sales GROUP BY day, sales_person_from;
)
GROUP BY day, sales_person;
This is too slow and looks ugly. I am seeking a better solution. By the way the database being used in Oracle and I have no control over database except that I can run queries against it.
There is no need to aggregate twice, and the union operator typically does an implicit unique sort which, again, in not necessary in your case.
SELECT day, sales_person, sum(num_transactions) from
(
SELECT day, sales_person, number_of_transactions As num_transactions FROM sales;
UNION ALL
SELECT day, sales_person_from, number_of_transactions FROM internal_sales;
)
GROUP BY day, sales_person;
Removing the intermediate aggregation and the unique sort should help a bit.
Related
First of all, I wish to say hi to the community here. The posts here have been a great help with VBA but this is my first question ever. I have a task that I need to solve in SQL (in MS Access) but it's sort of new to me and the task seems to be too complex.
I have a table in Access with the following structure(let's call it Tinvoices):
invoice | year | date | company | step | agent
5110001 | 2019 | 15/01/2019 | 1201 | 0 | John
5110001 | 2019 | 15/01/2019 | 1201 | 1 | Jack
5110002 | 2019 | 10/02/2019 | 1202 | 0 | John
5110002 | 2019 | 10/02/2019 | 1202 | 1 | Jack
5110002 | 2019 | 10/02/2019 | 1202 | 2 | Daniel
5110002 | 2019 | 10/02/2019 | 1202 | 3 | John
5110003 | 2019 | 12/03/2019 | 1205 | 0 | Jack
5110003 | 2019 | 12/03/2019 | 1205 | 1 | Daniel
5110003 | 2019 | 12/03/2019 | 1205 | 2 | David
This table relates to actions on invoices. Invoices and their related data are repeated with each step.
There is another table, which contains agents belonging to a certain department (let's call it Tdeptusers):
agent
John
Jack
What I need to do is the following. Have distinct lines for the invoices (the most unique key is combining the invoice, year and company) and counting in separate steps have been done by users in the Tdeptusers table and how many by users who are not in Tdeptusers. Something like this:
invoice | year | month | company | actionsByOurDept | actionsByOthers
5110001 | 2019 | 1 | 1201 | 2 | 0
5110002 | 2019 | 2 | 1202 | 3 | 1
5110003 | 2019 | 3 | 1205 | 1 | 2
I'm kind of a beginner, so you'll have to excuse me in providing usable codes. Being a complete beginner, I got stuck after the absolute basics. I have stuff like this:
SELECT
invoice,
year,
DatePart("m", Date) AS month,
company,
Sum(IIf(i.agent IN(d.agent), 1, 0)) AS actionsByOurDept,
Sum(IIf(i.agent IN(d.agent), 0, 1)) AS actionsByOthers
FROM Tinvoices AS i, Tdeptusers AS d
GROUP BY invoice, year, DatePart("m", Date), company;
This doesn't give back the desired result, mostly not in actionsByOthers, instead I get huge numbers. Maybe something similar to this solution might work but I haven't been able to do it.
Much appreciation for the help, folks.
Use proper standard explicit JOIN syntax :
SELECT i.invoice, year, DatePart("m", i.Date) AS month, i.company,
SUM( IIF(d.agent IS NOT NULL, 1, 0) ) AS actionsByOurDept,
SUM( IIf(d.agent IS NULL, 1, 0) ) AS actionsByOthers
FROM Tinvoices AS i LEFT JOIN
Tdeptusers AS d
ON d.agent = i.agent
GROUP BY i.invoice, i.year, DatePart("m", i.Date), i.company;
Use left join:
SELECT invoice, year, DatePart("m", Date) AS month, company,
COUNT(d.agent) AS actionsByOurDept,
SUM(IIF(d.agent IS NULL, 1, 0)) AS actionsByOthers
FROM Tinvoices AS i LEFT JOIN
Tdeptusers AS d
ON d.agent = i.agent
GROUP BY invoice, year, DatePart("m", Date), company;
You can directly count your department's users using COUNT().
I have a table named Sales:
+----------+-----------------+------------+
| Salesman | Sales Portfolio | Month |
+----------+-----------------+------------+
| Kavi | 12500 | 2018-01-05 |
| Kavi | 12500 | 2018-02-28 |
| Kavi | 12500 | 2018-03-20 |
| Raj | 21055 | 2018-01-05 |
| Raj | 32015 | 2018-02-28 |
| Raj | 12000 | 2018-03-20 |
+----------+-----------------+------------+
If a Sales Portfolio value is duplicated, remove all rows including itself from the table. In the example above, 12500 is duplicated, so remove all rows where Sales Portfolio = 12500.
Example expected output (only Raj displayed):
If you just want to display your expected output, then try the following:
WITH cte AS (
SELECT *,
COUNT(*) OVER (PARTITION BY Salesman, [Sales Portfolio]) cnt
FROM yourTable
)
SELECT
Salesman, [Sales Portfolio], Month
FROM cte
WHERE cnt = 1;
If you want to delete the non displaying records as well, then we can use the same CTE:
DELETE FROM cte WHERE cnt > 1;
I have two tables holding similar values, and I need to compare the two and find the differences between them:
SQL FIDDLE - http://sqlfiddle.com/#!6/7412e/9
Now you can see there is a difference between between the 2 tables for the figures in Jun-17.
AS you can see (as a total for everyone) table 1 has £75 for June but table 2 has £125 for june.
The result I'm looking for is when amounts are summed together and compared between tables on a monthly basis, if there is a difference in amount between the two tables I want it listed under 'Unknown'.
| MonthYear | Person | Amount | Month total
+-----------+--------+--------+--------------
| Jun-17 | Sam | 25 | 75(Table1)
| Sep-17 | Ben | 50 | 50(Table2)
| Jun-17 | Tom | 50 | 75(Table1)
| Jun-17 | Sam | 25 | 125(Table2)
| Sep-17 | Ben | 50 | 50(Table2)
| Jun-17 | Tom | 50 | 125(Table2)
| Jun-17 | | 50 | 125(Table2)
Now when there is a difference between the amount total over a month I want the difference to be classed as unknown
e.g
| MonthYear | Person | Amount | Month total
+-----------+--------+--------+--------------
| Jun-17 | Sam | 25 | 75(Table1)
| Sep-17 | Ben | 50 | 50(Table2)
| Jun-17 | Tom | 50 | 75(Table1)
| Jun-17 | Sam | 25 | 125(Table2)
| Sep-17 | Ben | 50 | 50(Table2)
| Jun-17 | Tom | 50 | 125(Table2)
| Jun-17 | Unknown| 50 | 125(Table2)
I understand that you could create a case when the person is null to display unknown but i need it to be specifically calculated on the difference between the 2 tables on a monthly calculation.
Does this make sense to anyone, its really hard to explain.
Generally, in any FROM clause a table name can be replaced with another SELECT as long as you give it a corelation name (t1 and t2 in this one):
SELECT t1.MonthYear, t1.AmountT1, t2.AmountT2, t1.amountT1 - isnull(t2.amountT2, 0) as Unknown'
from
( SELECT
MonthYear,
SUM(Amount) AS [AmountT1]
FROM
Invoice
GROUP BY MonthYear) t1
left outer join
( SELECT
MonthYear,
SUM(Amount) AS [AmountT2]
FROM
Invoice2
GROUP BY MonthYear) t2 on t2.MonthYear = t1.MonthYear
Below is my table:
practice_id | practice_name | practice_location | practice_monthly_revenue | practice_no_of_patients | date
-------------+-------------------+-------------------+--------------------------+-------------------------+---------------------
6 | Practice Clinic 1 | Location1 | 10000 | 8 | 2016-01-12 00:00:00
7 | Practice Clinic 1 | Location1 | 12000 | 10 | 2016-02-12 00:00:00
8 | Practice Clinic 1 | Location1 | 8000 | 4 | 2016-03-12 00:00:00
9 | Practice Clinic 1 | Location1 | 15000 | 10 | 2016-04-12 00:00:00
10 | Practice Clinic 1 | Location1 | 7000 | 3 | 2016-05-12 00:00:00
11 | Practice Clinic 2 | Location2 | 15000 | 12 | 2016-01-13 00:00:00
12 | Practice Clinic 2 | Location2 | 9000 | 8 | 2016-02-13 00:00:00
13 | Practice Clinic 2 | Location2 | 5000 | 2 | 2016-03-03 00:00:00
14 | Practice Clinic 2 | Location2 | 12000 | 9 | 2016-04-13 00:00:00
----------------------------------------------------------------------------------------------------------------------------------
I am firing below query to get monthly revenue vs monthly goal:-
select [date:month], SUM(practice_monthly_revenue) as Monthly_Revenue, 100000/12 as Goals
from practice_info
where practice_name IN ('Practice Clinic 1')
group by [date:month], practice_name
ORDER BY [date:month] ASC
Where "Monthly_Revenue" refers to exact revenue every month while Goal was the exact revenue expected to be generated.
Now I am having issue to write a sql query to adjust the goals next month if the goals aren't met.
E.g. if in March the revenue generated is below 8k which is the monthly goal then the remaining amount in goal should be adjusted in next months goal.
Will it be possible to achieve this with a sql query or I will have to write a sql procedure for it?
EDIT:- I forgot to add that the db belong to postgres.
Goals can be counted as
with recursive goals(mon, val, rev) as
(select min([pinf.date:month]) as mon /* Starting month */, 8000 as val /* Starting goal value */, pinf.practice_monthly_revenue as rev
from practice_info pinf
where pinf.practice_name IN ('Practice Clinic 1')
union all
select goals.mon + 1 as mon, 8000 + greatest(0, goals.val - goals.rev) as val, pinf.practice_monthly_revenue as rev
from practice_info pinf, goals
where goals.mon + 1 = [pinf.date:month]
and pinf.practice_name IN ('Practice Clinic 1')
)
select * from goals;
Just integrate it with your query to compare goals and revenues. It can be not exactly what you want, but I do believe you'll get the main point.
I have the following table in MS Access 2007:
customer | Promotion | Month | activator | request_date | activation_date
1 | promo1 | 10 | shop1 | 11/10/2011 | 21/10/2011
2 | promo2 | 9 | shop1 | 10/09/2011 | 15/09/2011
3 | promo2 | 9 | shop2 | 10/09/2011 | 16/09/2011
4 | promo1 | 10 | shop1 | 12/10/2011 | 13/10/2011
What I need is a query to calculate the average number of days that each shop takes to activate each promotion grouped by month. So for example one result would be:
shop1 in October took an average of 10+1/2 days to activate promo1.
Thanks in advance!
SELECT activator, Month, Promotion, AVG(activation_date - request_date)
FROM ...
GROUP BY activator, Month, Promotion
Try this:
select
activator,
[month],
promotion,
avg(convert( float, datediff(DAY, request_date,activation_date))) as avgTime
from dbo.Table1
group by activator,[month], promotion