Querying 2 Views on a Join - answers on one half being duplicated - sql

I have 2 views one holding inbound calls and the other outbound calls. I want my query to join the 2 views so that the inbound and outbound stand side by side for each operator (destinationname and originationname). At the moment my current query duplicates one half of the join, in the example below the inbound.
SELECT i.destinationname, i.volumein as inbound, o.volumeout as outbound,
i.year, i.month
FROM InboundCalls i
inner join OutboundCalls o
on i.destinationname = o.originationname
GROUP BY i.year, i.month, i.destinationname, o.volumeout, i.volumein
DestinationName Inbound Outbound Year Month
Accounts Spare 9 33 2016 8
Accounts Spare 9 9 2016 8
Accounts Spare 9 7 2016 8
Accounts Spare 9 38 2016 8
Accounts Spare 21 33 2016 9
Accounts Spare 21 9 2016 9
Accounts Spare 21 7 2016 9
Accounts Spare 21 38 2016 9
The result I am looking for will be similar to the below;
DestinationName Inbound Outbound Year Month
Accounts Spare 84 210 2016 9
Accounts Spare 12 32 2016 11
Accounts Spare 36 103 2016 10
Steve Jones 36 96 2016 8
Wayne Rooney 162 172 2016 8
Alan Shearer 1 216 2016 9
Alan Shearer 74 82 2016 8
Please let me know if this needs clarifying.

The reason for wrong results is that you join only on destination name, not on year and month.
First, you need to join not only on DestinationName but on the Year and Month as well. If the views have one row per distinct destination name, year and month, then you can get rid of the GROUP BY as well.
Second, you probably need a FULL JOIN instead of an INNER JOIN, assuming that you want results when there are only Incoming but not Outgoing data (and vice versa) for some month.
SELECT
COALESCE(i.destinationname, o.destinationname) AS DestinationName
COALESCE(i.volumein, 0) AS InBound,
COALESCE(o.volumeout, 0) AS OutBound,
COALESCE(i.year, o.year) AS Year,
COALESCE(i.month, o.month) AS Month
FROM InboundCalls AS i
FULL JOIN OutboundCalls AS o
ON i.destinationname = o.originationname
AND i.year = o.year
AND i.month = o.month ;

If I understand correctly you would want the output to be this:
DestinationName Inbound Outbound Year Month
Accounts Spare 9 87 2016 8
In which case I think the reason you are getting duplicates is because you are grouping by o.volumeout and i.volumein.
If you want a single row for each month and year then you would group by destinationame, mont and year and then get your totals by using SUM e.g.
SELECT i.destinationname, sum(i.volumein) as inbound, sum(o.volumeout) as outbound,
i.year, i.month
FROM InboundCalls i
inner join OutboundCalls o
on i.destinationname = o.originationname
GROUP BY i.year, i.month, i.destinationname

Related

SQL - Group by the base of an ID

My SQL is not really good, but I am improving.
I try to extract records from a table with sales data. I want to know how much profit was made by a retailer and its subsidiaries per month.
The retailer_id is build from the root of 5 digits and (if subsidiaries exist) an adjacent _ with two digits. Like so:
without subsidiaries: 30000
with subsidiaries: 30000_01, 30000_02
Code:
SELECT
retailer_id,
MONTH(Date(created_at)) AS month,
SUM(grand_total) AS Totals
FROM
sales_table
GROUP BY
retailer_id, month
As you can imagine, the retailer with subsidiaries are still separated line items.
As requested, I will give an example:
raw data
retailer_id
month
grand total
10006
12
10
10006
9
20
10006
9
40
10006_10
12
40
10015
9
10
10015
11
10
10015
12
5
10015
11
20
expected result:
retailer_id
month
Totals
10006
12
50
10006
9
60
10015
9
10
10015
11
30
10015
12
5
10015
11
20
Thank you for your help!
The answer is 'left'. As in this one:
select
left(retailer_id, 5),
Month(Date(created_at)) AS month,
sum(grand_total) AS Umsatz
FROM sales_order
WHERE store_id = '2' AND NOT status = 'canceled' AND created_at between '2021-09-01' AND '2022-01-27'
GROUP BY left(retailer_id, 5), month
ORDER BY left(retailer_id,5);

How Do I retrieve most Recent record in different years With Date date in different table

I'm working with a database that isn't structured that well and need to retrieve the row with the latest month used in specific years. The main data is stored is stored in the member table and lists one row per member month. The Date for the member month is not specifically stored here but connected by a foreign Date_Key and linked to a Date table. This is where the column for the Year and Month can be derived based on the Date_Key specified in each table. Each row in the Date table represents 1 new month for a year and each of these rows has a unique sequential date_key.
I am using Microsoft SQL Server Studio as the environment
Member Table
MemberKey
Membe_ID
Date_Key
100
1234
89
101
1234
96
102
1234
97
103
1236
96
104
1236
97
Date Table
Date_Key
Year
Month
89
2020
10
90
2020
11
91
2020
12
92
2021
1
93
2021
2
94
2021
3
95
2021
4
96
2021
5
97
2021
6
Looking for the following Results
Member_ID
Year
Month
1234
2020
10
1234
2021
6
1236
2021
6
2020/11 is NOT a date. It is a year/month pair. But it seems like a simple aggregate - select year, max(month) group by year. You join and include member ID so you include that column in the GROUP BY clause to get one row per member per year.
select mbr.Member_ID, dts.Year, max(dts.Month) as Month
from dbo.Members as mbr
inner join dbo.Dates as dts on mbr.Date_Key = dts.Date_Key
group by mbr.Member_ID, dts.Year
order by mbr.Member_ID, dts.Year
;

MS Access SQL – How can I count the occurrence of a number from one table in strings of another table

In MS Access 365 I have 2 tables and I want to count the occurrence of a year from the first table in part of a string of the second table, purely with SQL (so far I used VBA, but I want to simplify).
The first table (tDistinctYears) contains all the years, in which one of our members paid:
ID
PaymentYear
1
2015
2
2016
3
2017
3
2018
4
2019
5
2020
6
2021
7
2022
The second table (tPayments) has all payments from members with one column containing a membership number and the other one containing payment years. Sometimes a member pays for one year, sometime for several years. The table therefore looks like that:
MembershipNr
YearPayment
11
2016
11
2017
11
2018
26
2017
26
2018;2019
26
2020;2021;2022
38
2016
38
2017
38
2018;2019;2020;2021
I want a query which tells me how many members paid in which year:
PaymentYear
Count
2015
0
2016
2
2017
3
2018
3
2019
2
2020
2
2021
2
I used the following SQL query, which I found using various answers on stackoverflow:
SELECT tDistinctYears.PaymentYear, (COUNT(tPayments.YearPayment)) AS [Count]
FROM tDistinctYears
LEFT JOIN tPayments ON tDistinctYears.PaymentYear like "*" & tPayments.YearPayment & "*"
WHERE (tDistinctYears.PaymentYear > 0 AND tDistinctYears.PaymentYear <= YEAR(NOW()))
GROUP BY tDistinctYears.PaymentYear;
But what I get is this:
PaymentYear
Count
2015
0
2016
2
2017
3
2018
1
2019
0
2020
0
2021
0
It seems as if the above query does not use the “like” expression in the JOIN ON section.
Can someone help me, please?
I think you are close just alter column in where condition tPayments.YearPayment should be first and tDistinctYears.PaymentYear should be inside like operator.
SELECT tDistinctYears.PaymentYear, (COUNT(tPayments.YearPayment)) AS [Count]
FROM tDistinctYears
LEFT JOIN tPayments ON tPayments.YearPayment like "*" &
tDistinctYears.PaymentYear
& "*" WHERE (tDistinctYears.PaymentYear > 0 AND tDistinctYears.PaymentYear <=
YEAR(NOW()))
GROUP BY tDistinctYears.PaymentYear;

How to perform multiple table calculation with joins and group by

I have two tables client and grouping. They look like this:
Client
C_id
C_grouping_id
Month
Profit
Grouping
Grouping_id
Month
Profit
The client table contains monthly profit for every client and every client belongs to a specific grouping scheme specified by C_grouping_id.
The grouping table contains all the groups and their monthly profits.
I'm struggling with a query that essentially calculates the monthly residual for every subscriber:
Residual= (Subscriber Monthly Profit - Grouping monthly Profit)*(average subscriber monthly profits for all months / average profits for all months for the grouping subscriber belongs to)
I have come up with the following query so far but the results seem to be incorrect:
SELECT client.C_id, client.C_grouping_Id, client.Month,
((client.Profit - grouping.profit) * (avg(client.Profit)/avg(grouping.profit))) as "residual"
FROM client
INNER JOIN grouping
ON "C_grouping_id"="Grouping_id"
group by client.C_id, client.C_grouping_Id,client.Month, grouping.profit
I would appreciate it if someone can shed some light on what I'm doing wrong and how to correct it.
EDIT: Adding sample data and desired results
Client
C_id C_grouping_id Month Profit
001 aaa jul 10$
001 aaa aug 12$
001 aaa sep 8$
016 abc jan 25$
016 abc feb 21$
Grouping
Grouping_id Month Profit
aaa Jul 30$
aaa aug 50$
aaa Sep 15$
abc Jan 21$
abc Feb 27$
Query Result:
C_ID C_grouping_id Month Residual
001 aaa Jul (10-30)*(10/31.3)=-6.38
... and so on for every month for avery client.
This can be done in a pretty straight forward way.
The main difficulty is obviously that you try to deal with different levels of aggregation at once (average of the group and the client as well as the current record).
This is rather difficult/clumsy with simple SELECT FROM GROUP BY-SQL.
But with analytical functions aka Window functions this is very easy.
Start with combining the tables and calculating the base numbers:
select c.c_id as client_id,
c.c_grouping_id as grouping_id,
c.month,
c.profit as client_profit,
g.profit as group_profit,
avg (c.profit) over (partition by c.c_id) as avg_client_profit,
avg (g.profit) over (partition by g.grouping_id) as avg_group_profit
from client c inner join grouping g
on c."C_GROUPING_ID"=g."GROUPING_ID"
and c. "MONTH" = g. "MONTH";
With this you already get the average profits by client and by grouping_id.
Be aware that I changed the data type of the currency column to DECIMAL (10,3) as a VARCHAR with a $ sign in it is just hard to convert.
I also fixed the data for MONTHS as the test data contained different upper/lower case spellings which prevented the join to work.
Finally I turned all column names into upper case to, in order to make typing easier.
Anyhow, running this provides you with the following result set:
CLIENT_ID GROUPING_ID MONTH CLIENT_PROFIT GROUP_PROFIT AVG_CLIENT_PROFIT AVG_GROUP_PROFIT
16 abc JAN 25 21 23 24
16 abc FEB 21 27 23 24
1 aaa JUL 10 30 10 31.666
1 aaa AUG 12 50 10 31.666
1 aaa SEP 8 15 10 31.666
From here it's only one step further to the residual calculation.
You can either put this current SQL into a view to make it reusable for other queries or use it as a inline view.
I chose to use it as a common table expression (CTE) aka WITH clause because it's nice and easy to read:
with p as
(select c.c_id as client_id,
c.c_grouping_id as grouping_id,
c.month,
c.profit as client_profit,
g.profit as group_profit,
avg (c.profit) over (partition by c.c_id) as avg_client_profit,
avg (g.profit) over (partition by g.grouping_id) as avg_group_profit
from client c inner join grouping g
on c."C_GROUPING_ID"=g."GROUPING_ID"
and c. "MONTH" = g. "MONTH")
select client_id, grouping_id, month,
client_profit, group_profit,
avg_client_profit, avg_group_profit,
round( (client_profit - group_profit)
* (avg_client_profit/avg_group_profit), 2) as residual
from p
order by grouping_id, month, client_id;
Notice how easy to read the whole statement is and how straight forward the residual calculation is done.
The result is then this:
CLIENT_ID GROUPING_ID MONTH CLIENT_PROFIT GROUP_PROFIT AVG_CLIENT_PROFIT AVG_GROUP_PROFIT RESIDUAL
1 aaa AUG 12 50 10 31.666 -12
1 aaa JUL 10 30 10 31.666 -6.32
1 aaa SEP 8 15 10 31.666 -2.21
16 abc FEB 21 27 23 24 -5.75
16 abc JAN 25 21 23 24 3.83
Cheers,
Lars

SQL count ocurrences within time periods

I recently started up working with Access database in a project at my work (hospital) and got lots of useful tips from this site. However, I have now a problem that I can't figure out how to solve.
I have a table containing treatment dates (as well as other data) for lots of patients. My task is to count the number of treatments given within each week (and possibly month/quarter/year as well). The treatment dates are located in the column 'TreatDate' in the table 'Data'.
I have used DatePart to separate out year and week number as:
SELECT
DatePart('yyyy',[TreatStart]) AS Year, DatePart('ww',[TreatStart]) AS Week
FROM Data
ORDER BY DatePart('yyyy',[TreatStart]),DatePart('ww',[TreatStart]);
Which gives me:
Year Week
2006 16
2006 16
2006 16
2006 17
2006 17
2006 18
2006 19
2006 19
2006 19
... ...
How do I count the number of occurances in each week in order to get something like:
Year Week N
2006 16 3
2006 17 2
2006 18 1
2006 19 3
... ... ...
Best regards,
Mattias
Just add the group by on the same values by which you want count and add the count aggregate function to get the total count of the appointments at that given group.
SELECT DatePart('yyyy',[TreatStart]) AS Year, DatePart('ww',[TreatStart]) AS Week , COUNT(*) As N
FROM Data
GROUP BY DatePart('yyyy',[TreatStart]),DatePart('ww',[TreatStart])
ORDER BY DatePart('yyyy',[TreatStart]),DatePart('ww',[TreatStart]);
Use GROUP BY and COUNT:
SELECT DatePart('yyyy',[TreatStart]) AS Year,
DatePart('ww',[TreatStart]) AS Week,
COUNT(*) as N
FROM Data
GROUP BY DatePart('yyyy',[TreatStart]),DatePart('ww',[TreatStart])
ORDER BY DatePart('yyyy',[TreatStart]),DatePart('ww',[TreatStart]);
The output will be:
Year Week N
2006 16 3
2006 17 2
2006 18 1
2006 19 3