How to join two tables in one view? - sql

I am trying to create a view where I can see the items that have been planned to be shipped and have not been shipped, and the items have have been shipped but were not planned to.
In order to do this I have 2 tables with different data in them.
Table SC (actually shipped):
+---------+-----------------+----------------------+-------------+
| item_id | source_location | destination_location | shipped_qty |
+---------+-----------------+----------------------+-------------+
| 001 | California | South_Carolina | 80 |
+---------+-----------------+----------------------+-------------+
| 001 | California | South_Carolina | 0 |
+---------+-----------------+----------------------+-------------+
| 001 | California | Texas | 20 |
+---------+-----------------+----------------------+-------------+
| 003 | Texas | South_Carolina | 200 |
+---------+-----------------+----------------------+-------------+
| 004 | South_Carolina | Texas | 30 |
+---------+-----------------+----------------------+-------------+
| 004 | South_Carolina | Texas | 10 |
+---------+-----------------+----------------------+-------------+
Table SO (plan to ship items):
+---------+-----------------+----------------------+---------------+
| item_id | source_location | destination_location | planned_order |
+---------+-----------------+----------------------+---------------+
| 001 | California | South_Carolina | 100 |
+---------+-----------------+----------------------+---------------+
| 001 | California | South_Carolina | 100 |
+---------+-----------------+----------------------+---------------+
| 001 | California | Texas | 10 |
+---------+-----------------+----------------------+---------------+
| 003 | Texas | South_Carolina | 200 |
+---------+-----------------+----------------------+---------------+
| 004 | South_Carolina | Texas | 300 |
+---------+-----------------+----------------------+---------------+
| 004 | South_Carolina | Texas | 50 |
+---------+-----------------+----------------------+---------------+
So in this case, for example, since the item 001 has three different planned orders from California to South Carolina, I don't want it to show all the three orders in the view, I want it to be only in one row, but sum all the planned orders together, as showed below.
Desired Outcome:
+---------+----------------+-----------------+-------------+-------------+
| item_id | source_loc | destination_loc | shipped_qty | planned_qty |
+---------+----------------+-----------------+-------------+-------------+
| 001 | California | South_Carolina | 80 | 200 |
+---------+----------------+-----------------+-------------+-------------+
| 001 | California | Texas | 20 | 10 |
+---------+----------------+-----------------+-------------+-------------+
| 003 | Texas | South_Carolina | 200 | 200 |
+---------+----------------+-----------------+-------------+-------------+
| 004 | South_Carolina | Texas | 40 | 350 |
+---------+----------------+-----------------+-------------+-------------+
I have tried this so far:
SELECT o.source_location,
o.destination_location,
o.item_id,
o.planned_order,
c.shipped_qty
FROM SO_TRANSFER o, SC_TRANSFER c
But this hasn't worked since the shipped_qty does not match the item and this code also does not add the orders together.
By the way, I am using Microsoft SQL Server 2012.
Thank you!

I think you want:
select coalesce(s.item_id, p.item_id) as item_id,
coalesce(s.source_location, p.source_location) as source_location,
coalesce(s.destination_location, p.destination_location) as destination_location,
coalesce(s.shipped_qty, 0) as shipped_qty,
coalesce(planned_qty, 0) as planned_qty
from (select item_id, source_location, destination_location, sum(shipped_qty) as shipped_qty
from sc
group by item_id, source_location, destination_location
) s full join
(select item_id, source_location, destination_location, sum(planned_qty) as planned_qty
from so
group by item_id, source_location, destination_location
) p
on s.item_id = p.item_id and
s.source_location = p.source_location and
s.destination_location = p.destination_location;

You can try this:
SELECT A.item_id,
A.source_location AS source_loc,
A.destination_location AS destination_loc,
A.shipped_qty,
B.planned_order
FROM
(SELECT item_id,source_location,destination_location,SUM(shipped_qty)
AS shipped_qty
FROM SC GROUP BY item_id,source_location,destination_location) A,
(SELECT item_id,source_location,destination_location,SUM(planned_order)
AS planned_order
FROM SO GROUP BY item_id,source_location,destination_location) B
WHERE A.item_id = B.item_id AND
A.source_location= B.source_location AND
A.destination_location= B.destination_location
EDIT: I just realize my answer is similar to Gordon Linoff's answer, and his answer got more feature such as handling data that exists in one table only using COALESCE in T-SQL and FULL JOIN property. Since I worked 1 hour for this answer, so I will just leave it here.

The point is you should SUM(quantity) from each table first, then you can easy JOIN 2 table with condition:
ON so.item_id = sc.item_id AND so.source_loc = sc.source_loc AND so.destination_loc = sc.destination_loc

The following query will do what you are looking for
Create table #SC(
item_id varchar(50),
source_location varchar(max),
Destination_location varchar(max),
Shipped_qty int)
insert into #SC values('001','california','sourth_carolian',80)
insert into #SC values('001','california','sourth_carolian',0)
insert into #SC values('001','california','Texas',20)
insert into #SC values('003','Texas','sourth_carolian',200)
insert into #SC values('004','sourth_carolian','Texas',30)
insert into #SC values('004','sourth_carolian','Texas',10)
--select * from #SC
Create table #SO(
item_id varchar(50),
source_location varchar(max),
Destination_location varchar(max),
Planned_order int)
insert into #SO values('001','california','sourth_carolian',100)
insert into #SO values('001','california','sourth_carolian',100)
insert into #SO values('001','california','Texas',10)
insert into #SO values('003','Texas','sourth_carolian',200)
insert into #SO values('004','sourth_carolian','Texas',300)
insert into #SO values('004','sourth_carolian','Texas',50)
--select * from #SO
select C.item_id,C.source_location,C.Destination_location, sum(C.Shipped_qty) as Shipped_qty, po.planned_order from #SC C
outer apply
(select sum(Planned_order) as planned_order from #SO
where source_location+Destination_location=C.source_location+C.Destination_location
group by item_id,source_location,Destination_location ) as PO
group by C.item_id,C.source_location,C.Destination_location,po.planned_order

You can create a SELECT statement, and use that as a table in the FROM clause:
SELECT o.source_location,
o.destination_location,
o.item_id,
o.planned_order,
c.shipped_qty_sum
FROM SO_TRANSFER o
INNER JOIN (SELECT SUM(shipped_qty) AS shipped_qty_sum,
source_location,
item_id
FROM SC_TRANSFER
GROUP BY source_location, item_id) c
ON o.item_id = c.item_id AND o.source_location = c.source_location

Related

SQL Group By and Join based on a weird client table

I have 3 tables that I want to join together and group it to get client membership info. My code works for grouping the base table together but it breaks at the join part and I can't figure out why.
BASE TABLE : sales_detail
+-------+-----------+-----------+-----------------------------------------+
| order_date | transaction_id| product_cost | payment_type | country
+-------+-----------+-----------+------------------------------------------+
| 10/1 | 12345 | 20 | mastercard | usa
| 10/1 | 12345 | 50 | mastercard | usa
| 10/5 | 82456 | 50 | mastercard | usa
| 10/9 | 64789 | 30 | visa | canada
| 10/15 | 08546 | 20 | mastercard | usa
| 10/15 | 08546 | 90 | mastercard | usa
| 10/17 | 65898 | 50 | mastercard | usa
+-------+-----------+-----------+-------------------------------------+
table : client_information
+-------+-----------+-----------+-------------------+
| other_id | client_Type | item
+-------+-----------+-----------+----------+
| 112341 | new | hola |
| 112341 | old | mango |
| 145634 | old | pine |
| 879547 | old | vip |
| 745688 | new | unio |
| 745688 | old | dog |
| 147899 | new | cat |
| 124589 | new | amigo |
+-------+-----------+-----------+-----------+
table : connector
+-------+-----------+-----------+-------------------+
| transaction_ID | other_id | item
+-------+-----------+-----------+----------+
| 12345 | 112341 | hola |
| 82456 | 145634 | pine |
| 08157 | 879547 | unio |
| 08546 | 745688 | dog |
| 65898 | 147899 | cat |
| 06587 | 124589 | amigo |
+-------+-----------+-----------+-----------+
**I want the output to look something like this: **
IDEAL OUTPUT
+-------+-----------+-----------+--------------------------------+
| order_date | transaction_ID | product_cost | client_Type|
+-------+-----------+-----------+--------------------------------+
| 10/1 | 12345 | 70 | new |
| 10/5 | 82456 | 70 | old |
| 10/15 | 08546 | 110 | old |
| 10/17 | 65898 | 50 | new |
+-------+-----------+-----------+----------------------------------+
**i am trying to join my base table to the connector table by transaction ID to get other_id and items to match to client_type **
This is the code i used but it failed to compile after adding in left joins :
select t1.transaction_id, sum(t1.product_cost), t1.order_date, t3.client_type
from sales_detail t1
left join (select DISTINCT transaction_ID, other_id, fruits from connector) t2
ON t1.transaction_ID=t2.transaction_ID
left join (select DISTINCT order_id, client_type, fruits from client information) t3
ON t2.other_id=t3.other_id and t2.item=t3.item
where t1.payment_type='mastercard' and t1.order_Date between '2020-10-01' and'2020-10-31'
and country != 'canada'
GROUP BY t1.transaction_id, t1.order_date, t3.client_type;
Thanks in advance! I am a beginner so still learning the ins and outs of sql! (am using hive)
I think that's joins and aggregation. For more efficiency, you can pre-aggregate in a subquery, then join:
select sd.*, ci.client_type
from (
select order_date, transaction_id, sum(product_cost) product_cost
from sales_detail
where
payment_type = 'mastercard'
and order_date >= '2020-10-01'
and order_date < '2020-11-01'
and country <> 'canada'
group by order_date, transaction_id
) sd
inner join connector c on c.transaction_id = sd.transaction_id
inner join client_information ci on ci.other_id = c.other_id
Note that I rewrote the filter on order_date to use half-open intervals rather than between. This properly handles the case when your dates have a time portion.
From what I have understood, your code works although not as you would like using an INNER JOIN and it fails to add a LEFT JOIN. I think what happens is a failure due to the NULL elements. to add a NULL element and not get an error, you have to use some function that changes the NULL value to 0 .
One such function is the ISNULL(yourColumn, 0) function of T-SQL.The documentation.
I can see that in result table you only need clients who used mastercard, so you should use inner join there so only those client who used mastercard will be considered. While the remaining query is okay i guess, but main problem was the join on client information.
I think on the answer with GMB you also need to join on item column otherwise you will get multiple rows output.
select sd.*, ci.client_type
from (
select order_date, transaction_id, sum(product_cost) product_cost
from sales_detail
group by order_date, transaction_id
) sd
inner join connector c on c.transaction_id = sd.transaction_id
inner join client_information ci on ci.other_id = c.other_id and ci.item = c.item
Just modify with your filters and you should be sorted.

eSQL multiple join but with conditions

I've 3 tables as under
MERCHANDISE
+-----------+-----------+---------------+
| MERCH_NUM | MERCH_DIV | MERCH_SUB_DIV |
+-----------+-----------+---------------+
| 1 | car | awd |
| 1 | car | awd |
| 2 | bike | 1kcc |
| 3 | cycle | hybrid |
| 3 | cycle | city |
| 4 | moped | fixie |
+-----------+-----------+---------------+
PRIORITY
+----------+-----------+---------+---------+------------+------------+---------------+
| CUST_NUM | SALES_NUM | DOC_NUM | BALANCE | PRIORITY_1 | PRIORITY_2 | PRIORITY_CODE |
+----------+-----------+---------+---------+------------+------------+---------------+
| 90 | 1000 | 10 | 23 | 1 | 6 | NO |
| 91 | 1001 | 20 | 32 | 3 | 7 | PRI |
| 92 | 1002 | 30 | 11 | 2 | 8 | LATE |
| 93 | 1003 | 40 | 22 | 5 | 9 | 1MON |
+----------+-----------+---------+---------+------------+------------+---------------+
ORDER
+----------+-----------+---------+---------+-----------+-----------+
| CUST_NUM | SALES_NUM | DOC_NUM | COUNTRY | MERCH_NUM | MERCH_DIV |
+----------+-----------+---------+---------+-----------+-----------+
| 90 | 1000 | 10 | INDIA | 1 | car |
| 91 | 1001 | 20 | CHINA | 2 | bike |
| 92 | 1002 | 30 | USA | 3 | cycle |
| 93 | 1003 | 40 | UK | 4 | moped |
+----------+-----------+---------+---------+-----------+-----------+
I want to join the left joined table from the last two tables with the first one such that the MERCH_SUB_DIV 'awd' appears only once for each unique combination of merch_num and merch_div
the code I came up with is as under, but I'm not sure how do I eliminate the duplicate row just for the awd
select
ROW#, MERCH.MERCH_NUMBER, ORDPRI.MERCH_NUMBER, ORDPRI.CUST_NUM,
BALANCE, SALES_NUM, ITEM_NUM, RANK, PRIORITY_1
from (
select
ROW_NUMBER() OVER(
PARTITION BY ORD.DOC_NUM, ORD.ITEM_NUM
ORDER BY ORD.DOC_NUM, ORD.ITEM_NUM ASC
) AS Row#,
ORD.CUST_NUM, PRI.CUST_NUM, ORD.MERCH_NUM, ORD.MERCH_DIV, PRI.BALANCE,
pri.DOC_NUM, pri.SALES_NUM, pri.PRIORITY_1, pri.PRIORITY_2
from ORDER as ORD
left join PRIORITY as PRI on ORD.DOC_NUM = PRI.DOC_NUM
and ORD.SALES_NUMBER = PRI.SALES_NUM
where country_name in ('USA', ‘INDIA’)
) as ORDPRI
left join MERCHANDISE as MERCH on ORDPRI.DIV = MERCH.DIV
and ORDPRI.MERCH_NUM = MERCH.MERCH_NUM
You have to use 'DISTINCT' keyword to get unique values, but if your 'Priority table' & 'Order table' contains different values for Same MERCH_NUM then the final result contains the repetation of the 'MERCH_NUM'.
SELECT DISTINCT M.MERCH_NUMBER, O.MERCH_NUMBER, O.CUST_NUM, BALANCE, SALES_NUM,ITEM_NUM,RANK,PRIORITY_1
FROM priority_table P
LEFT JOIN order_table O ON P.CUST_NUM = O.CUST_NUM AND P.SALES_NUM=O.SALES_NUM AND P.DOC_NUM = O.DOC_NUM
LEFT JOIN merchandise_table M ON M.MERCH_NUM = O.MERCH_NUM
A way around can be to add one new Row_Number() in the outermost query having Partition by MERCH_SUB_DIV + all the columns in the final list and then filter final results based on the New Row_Number() . Follows a pseudo code that might help:
select
-- All expected columns in final result except the newRow#
ROW#, MERCH_NUM, CUST_NUM,
BALANCE, SALES_NUM, PRIORITY_1
from (
select
ROW#,
-- the new row number includes all column you want to show in final result
row_number() over ( PARTITION BY MERCH.MERCH_SUB_DIV ,
MERCH.MERCH_NUM, ORDPRI.MERCH_NUM, ORDPRI.CUST_NUM,
BALANCE, SALES_NUM, PRIORITY_1
order by (select 1 )) as newRow# ,
MERCH.MERCH_NUM, ORDPRI.CUST_NUM,
BALANCE, SALES_NUM, PRIORITY_1
from (
-- main query goes here
select
ROW_NUMBER() OVER(
PARTITION BY ORD.DOC_NUM --, ORD.ITEM_NUM
ORDER BY ORD.DOC_NUM ASC --, ORD.ITEM_NUM
) AS Row#,
ORD.CUST_NUM, ORD.MERCH_NUM, ORD.MERCH_DIV as DIV, PRI.BALANCE,
pri.DOC_NUM, pri.SALES_NUM, pri.PRIORITY_1, pri.PRIORITY_2
from #ORDER as ORD
left join #PRIORITY as PRI on ORD.DOC_NUM = PRI.DOC_NUM
and ORD.SALES_NUMBER = PRI.SALES_NUM
where country_name in ('USA', 'INDIA')
) as ORDPRI
left join #MERCHANDISE as MERCH on ORDPRI.DIV = MERCH.DIV
and ORDPRI.MERCH_NUM = MERCH.MERCH_NUM
) as T
-- final filter to get distinct values
where newRow# = 1
Sample code here .. Hope this helps!!

How can I select from one table where the ID exists in one of two tables, but not in either of other two tables

This sounds confusing, but the idea is quite simple.
I want to get a list of products that have default rates, but a given 'Agent' doesn't have rates for. To do that, I need to select from the below table
t_Products
|-ProductID-|--Product-|
| 100 | Product1 |
| 101 | Product2 |
| 102 | product3 |
| 103 | product4 |
Where the ID exists in either t_Annual_DefaultCost or t_Daily_DefaultCost
t_Annual_DefaultCost
|-DefaultID-|-ProductID-|--Cost-|
| 100 | 100 | 24.00 |
| 101 | 101 | 26.00 |
t_Daily_DefaultCost
|-DefaultID-|-ProductID-|--Cost-|-Days-|
| 100 | 100 | 24.00 | 1 |
| 101 | 100 | 26.00 | 2 |
| 102 | 102 | 22.50 | 2 |
| 103 | 102 | 97.50 | 8 |
But it cannot exist in either t_Annual_AgentCost or t_Daily_AgentCost for the given Agent ID
t_Annual_AgentCost
|---CostID--|-ProductID-|-AgentID-|--Cost-|
| 100 | 100 | 10001 | 24.00 |
| 101 | 100 | 10001 | 20.00 |
t_Daily_AgentCost
|---CostID--|-ProductID-|-AgentID-|--Cost-|-Days-|
| 100 | 100 | 10001 | 24.00 | 1 |
| 102 | 102 | 10002 | 35.00 | 2 |
so for AgentID 10001 the end result should be
|-ProductID-|--Product-|
| 101 | product2 |
| 102 | product3 |
and for AgentID 10002 the end result should be
|-ProductID-|--Product-|
| 100 | product1 |
| 101 | product2 |
I'm currently using the below code to get a list of products that have default rates.
But I can't work out how to remove/not get the ones also in the AgentCost tables.
Select
distinct a.*
from
t_Products as a
inner join
(
select
DefaultID ,ProductID
from
t_Daily_DefalutCost
union
select
DefaultID , ProductID
from
t_Annual_DefaultCost
)
as b on a.ProductID = b.ProductID
If you want to do one Agent at a time, then this is how I would do it:
SELECT
a.*
FROM
t_Products As a
WHERE
( EXISTS( SELECT * FROM t_Daily_DefaultCost As d WHERE d.ProductID = a.ProductID )
OR EXISTS( SELECT * FROM t_Annual_DefaultCost As d WHERE d.ProductID = a.ProductID )
)
AND NOT
( EXISTS( SELECT * FROM t_Daily_AgentCost As d
WHERE d.ProductID = a.ProductID
AND d.AgentID = #SpecifedAgentID )
OR EXISTS( SELECT * FROM t_Annual_AgentCost As d
WHERE d.ProductID = a.ProductID
AND d.AgentID = #SpecifedAgentID )
)
The OR EXISTSs here work pretty much the same as UNION ALL SELECTs would .
I'd use this sort of approach.
select yourfields
from yourtables
where id in
(
(select id
from onetable
union
select id
from anothertable)
except
(select id
from yetanothertable
union
select id
from thefinaltable)
)
You can fill in the actual table names.

Fixing the payout issue

I am re-posting my original question with edits, as that question was answered and best answer chosen.
Payments comes from our supplier which goes towards the accounts and the reps get paid based on which account got how much.
Customers Table (Usage is kwH)
+----+----------+------------+----------+----------+----------+-------+-------+
| ID | Customer | Account_no | Meter_no | Supplier | Active | Usage | Repid |
+----+----------+------------+----------+----------+----------+-------+-------+
| 1 | Joe | 123 | 111 | NSTAR | active | 20 | 100 |
| 2 | Joe | 123 | 222 | NSTAR | active | 30 | 100 |
| 3 | Joe | 123 | 150 | NSTAR | inactive | 60 | 100 |
| 4 | Sam | 456 | 352 | SEP | active | 50 | 100 |
| 5 | Jill | 789 | 222 | FES | active | 40 | 200 |
| 6 | Mike | 883 | 150 | ABB | inactive | 40 | 200 |
+----+----------+------------+----------+----------+----------+-------+-------+
Payment_Receive (table)
+------------+----------+-------------+-------------+
| Account_no | Supplier | Amount_paid | PaymentDate |
+------------+----------+-------------+-------------+
| 123 | NSTAR | 20 | 2011-11-01 |
| 456 | SEP | 40 | 2011-11-01 |
| 456 | SEP | -40 | 2011-11-01 |
| 456 | SEP | 40 | 2011-11-01 |
| 789 | FES | 50 | 2011-11-01 |
| 883 | ABB | 30 | 2011-11-01 |
+------------+----------+-------------+-------------+
The two tables are used for rep payout. Payment are recieved for each account, they are matched with our customers based on Account_No and Supplier. We do not have control over the payment_table because it comes from outside. This creates certain problems because we can not do one-to-one match between the two tables. Leaving that aside, I would like to have payout calculated for RepID = 100 with certain criteria. This is the output I would like to see for RepId = 100
+------------+----------+-------------+-------------+-------------+
| Account_no | Supplier | Amount_paid | Usage | PaymentDate |
+------------+----------+-------------+-------------+-------------+
| 123 | NSTAR | 20 | 60* | 2011-11-01 |
| 456 | SEP | 40 | 50 | 2011-11-01 |
| 456 | SEP | -40 | 40 | 2011-11-01 |
| 456 | SEP | 40 | 40 | 2011-11-01 |
+------------+----------+-------------+-------------+-------------+
Note here that
Account_no 123 exists thrice in customers table, it must show one time in rep payout
3 amounts were paid to account_no 456, all the three must show in the report
*60 = Notice that there are 2 active records (and one inactive). This could be the sum of the two active. But any other value is acceptable if that makes the query easy (for greater of the two or one, not the other)
Note that Usage column must appear in the output table, This is the column that creates problem for me. If I dont include this everything works fine.
The point with Usage column, if I have two records for same customer having same Account_No and Supplier but different usage, that makes the two records distinct when I include usage column. Therefore distinct does not work to remove this duplicate.
Reports are calculated on Monthly basis
Script for the question
create database testcase
go
use testcase
go
create table customers (
id int not null primary key identity,
customer_name varchar(25),
account_no int,
meter_no int,
supplier varchar(20),
active varchar(20),
usage int,
repid int
)
create table payments_received (
account_no int,
supplier varchar(20),
amount_paid float,
paymentdate smalldatetime
)
insert into customers values('Joe',123, 111,'NSTAR','active',20,100)
insert into customers values('Joe',123, 222,'NSTAR','active',30, 100)
insert into customers values('Joe',123, 150,'NSTAR','inactive',60,100)
insert into customers values('Sam',456, 352,'SEP','active',40,100)
insert into customers values('Jill',789, 222,'FES','active',40,200)
insert into customers values('Mike',883, 150,'ABB','inactive',40,200)
select * from customers
insert into payments_received values(123,'NSTAR',20,'2011-11-01')
insert into payments_received values(456,'SEP',40,'2011-11-01')
insert into payments_received values(456,'SEP',-40,'2011-11-01')
insert into payments_received values(456,'SEP',40,'2011-11-01')
insert into payments_received values(789,'FES',50,'2011-11-01')
insert into payments_received values(883,'ABB',30,'2011-11-01')
select * from payments_received
How about this:
CREATE VIEW v_customers_by_rep
AS
SELECT SUM(USAGE) AS USAGE ,
REPID ,
CAST(account_no AS VARCHAR) + '_' + Supplier AS UniqueId
FROM customers
GROUP BY CAST(account_no AS VARCHAR) + '_' + Supplier ,
REPID
GO
DECLARE
#repid INT
SET #repid = 100
SELECT pr.* ,
u.Usage
FROM payments_received pr INNER JOIN v_customers_by_rep u
ON CAST(pr.account_no AS VARCHAR) + '_' + pr.Supplier = u.UniqueId
WHERE u.repid = #repid
You could also eliminate inactive records in the view if desired.

I need to fix payout issue - example given below (re-edited)

Customer Table (usage is kwH)
+----+----------+------------+----------+----------+----------+-------+-------+
| ID | Customer | Account_no | Meter_no | Supplier | Active | Usage | Repid |
+----+----------+------------+----------+----------+----------+-------+-------+
| 1 | Joe | 123 | 111 | NSTAR | active | 20 | 100 |
| 2 | Joe | 123 | 222 | NSTAR | active | 30 | 100 |
| 3 | Joe | 123 | 150 | NSTAR | inactive | 60 | 100 |
| 4 | Sam | 456 | 352 | SEP | active | 50 | 100 |
| 5 | Jill | 789 | 222 | FES | active | 40 | 200 |
| 6 | Mike | 883 | 150 | ABB | inactive | 40 | 200 |
+----+----------+------------+----------+----------+----------+-------+-------+
Payment_Receive (table)
+------------+----------+-------------+-------------+
| Account_no | Supplier | Amount_paid | PaymentDate |
+------------+----------+-------------+-------------+
| 123 | NSTAR | 20 | 2011-11-01 |
| 456 | SEP | 40 | 2011-11-01 |
| 456 | SEP | -40 | 2011-11-01 |
| 456 | SEP | 40 | 2011-11-01 |
| 789 | FES | 50 | 2011-11-01 |
| 883 | ABB | 30 | 2011-11-01 |
+------------+----------+-------------+-------------+
The two tables are use for rep payout. We do not have control over the payout_table because it comes from outside. This creates certain problems because we can not do one-to-one match between the two tables. Leaving that aside, I would like to have payout calculated for RepID = 100 with certain criteria. This is the output I would like to see for RepId = 100
+------------+----------+-------------+-------------+-------------+
| Account_no | Supplier | Amount_paid | Usage | PaymentDate |
+------------+----------+-------------+-------------+-------------+
| 123 | NSTAR | 20 | 60* | 2011-11-01 |
| 456 | SEP | 40 | 50 | 2011-11-01 |
| 456 | SEP | -40 | 40 | 2011-11-01 |
| 456 | SEP | 40 | 40 | 2011-11-01 |
+------------+----------+-------------+-------------+-------------+
Note here that
Account_no 123 exists twice in customers table, it must show one time in rep payout
3 amounts were paid to account_no 456, all the three must show in the report
Reports are calculated on Monthly basis
Script for example (Updated with Usage column)
create database testcase
go
use testcase
go
create table customers (
id int not null primary key identity,
customer_name varchar(25),
account_no int,
meter_no int,
supplier varchar(20),
active varchar(20),
usage int,
repid int
)
create table payments_received (
account_no int,
supplier varchar(20),
amount_paid float,
paymentdate smalldatetime
)
insert into customers values('Joe',123, 111,'NSTAR','active',20,100)
insert into customers values('Joe',123, 222,'NSTAR','active',30, 100)
insert into customers values('Joe',123, 150,'NSTAR','inactive',60,100)
insert into customers values('Sam',456, 352,'SEP','active',40,100)
insert into customers values('Jill',789, 222,'FES','active',40,200)
insert into customers values('Mike',883, 150,'ABB','inactive',40,200)
select * from customers
insert into payments_received values(123,'NSTAR',20,'2011-11-01')
insert into payments_received values(456,'SEP',40,'2011-11-01')
insert into payments_received values(456,'SEP',-40,'2011-11-01')
insert into payments_received values(456,'SEP',40,'2011-11-01')
insert into payments_received values(789,'FES',50,'2011-11-01')
insert into payments_received values(883,'ABB',30,'2011-11-01')
select * from payments_received
Updated: Updated The question and script
Usage been added to Customers table
Usage must appear int the result table
*60 = Notice that there are 2 active records (and one inactive). This could be the sum of the two, the one that is larger. This column is created problem removing duplicates
Two database brand independent options:
option 1:
Select
*
from
Payment_Receive PR
inner join
(select distinct Account_no, Supplier
From Customer where Repid = 100 ) C
on (PR.Account_no = C.Account_no
and PR.Supplier = C.Supplier )
option 2:
Select
*
from
Payment_Receive PR
Where exists
(select *
From Customer C
where
Repid = 100 and
PR.Account_no = C.Account_no and
PR.Supplier = C.Supplier )
with date range:
option 1:
Select
*
from
Payment_Receive PR
inner join
(select distinct Account_no, Supplier
From Customer where Repid = 100 ) C
on (PR.Account_no = C.Account_no
and PR.Supplier = C.Supplier )
where
year(PR.PaymentDate) = 2011 and
month(PR.PaymentDate) = 11
option 2:
Select
*
from
Payment_Receive PR
Where exists
(select *
From Customer C
where
Repid = 100 and
PR.Account_no = C.Account_no and
PR.Supplier = C.Supplier )
and
year(PR.PaymentDate) = 2011 and
month(PR.PaymentDate) = 11
I used a CTE to limit your Customer table, as well as added support for a specific YEAR and MONTH based on your question in a comment.
WITH customersCte AS
(
SELECT id, customer_name, account_no, meter_no, supplier, active, repid
, ROW_NUMBER() OVER (PARTITION BY account_no ORDER BY account_no ASC) AS rowNumber
FROM customers
)
SELECT pr.Account_no, pr.Supplier, pr.Amount_paid, pr.PaymentDate
FROM payments_received AS pr
INNER JOIN customersCte AS c ON pr.account_no = c.account_no
WHERE c.repid = 100
AND c.rowNumber = 1
AND YEAR(pr.PaymentDate) = 2011
AND MONTH(pr.PaymentDate) = 11