[Simplfying my tables] I have an excel model that outputs a forecast based on clients orders aggregated by month.
Table A
+----------+--------+----------+
| Customer | Volume | Date |
+----------+--------+----------+
| A | 100 | 1/1/2020 |
| B | 100 | 1/1/2020 |
| C | 100 | 1/1/2020 |
| A | 100 | 2/1/2020 |
| B | 100 | 2/1/2020 |
| C | 100 | 2/1/2020 |
+----------+--------+----------+
I want to join this data to live data coming from a database that is aggregated monthly by first date as well (i.e. if customer orders on 1/5/20 = 1/1/2020). Table B:
+----------+--------+----------+
| Customer | Volume | Date |
+----------+--------+----------+
| A | 100 | 1/1/2020 |
| A | 100 | 1/1/2020 |
| A | 100 | 1/1/2020 |
| B | 100 | 2/1/2020 |
| B | 100 | 2/1/2020 |
| Z | 10 | 2/1/2020 |
+----------+--------+----------+
I also have a third table that only has the Customer's first order date: Table C:
+----------+----------+--+
| Customer | Date | |
+----------+----------+--+
| A | 1/1/2020 | |
| B | 1/1/2020 | |
| C | 1/1/2020 | |
| Z | 2/1/2020 | |
+----------+----------+--+
I want to compare how the forecast (A) is tracking against the live data coming in (B), as well as add in the Customer's first Order Date (C). Since (B) is the most granular data, I am starting with this as my base. If I do a left join to (A) and (C) then some of Table A gets cut off (which I do not want); however my Table (C) data comes in correctly. If I do a full outer join to (A) then I do get the total forecast coming in correctly but the full outer join messes up Table (C). To add more complexity, Table B might have additional customers that were not part of the forecast so I need to see this data as well.
Forecast Number cut off:
select
b.customer,
b.volume,
b.date,
a.volume,
c.date
from b
left join a on b.customer = a.customer
left join c on c.customer = b.customer
Incorrect Data:
select
b.customer,
b.volume,
b.date,
a.volume,
c.date
from b
full outer join a on b.customer = a.customer
left join c on c.customer = b.customer
Use the coalesce function to join Table C with entries of both tables A and B:
select
coalesce(a.customer, b.customer),
b.volume,
b.date,
a.volume,
c.date
from b
full outer join a on b.customer = a.customer
left join c on c.customer = COALESCE(b.customer, a.customer)
Related
We have 3 main tables Order , Pickup and Delivery
Under one OrderID there are multiple Items (1 to many)
Some items in the order might have been picked up without delivery and vice versa.
Some order with all its items might have been picked up without a single delivery of its items and vice versa.
Some order all of its items will be picked up and delivered.
So I want to have a list of order ID then the picked up items for the order. if the order id not exists in pickup but exists in delivery that means the pickup is missing and show 'Not picked up'
and vise versa , If the item is in pickup but not in delivery that means delivery is missing and will show 'Not delivered'
From above cases you can see below sample data with the expected result
Order Table
OrderID
1
2
3
4
5
Pickup Table
OrderID | PickupItem | Date OrderID | DeliveryItem | Date
1 | 1100 | 13-02-2021 1 | 1100 | 14-02-2021
2 | 2200 | 06-02-2021 2 | 2201 | 05-02-2021
2 | 2201 | 06-02-2021 3 | 3300 | 03-02-2021
3 | 3300 | 04-02-2021 3 | 3301 | 03-02-2021
4 | 4400 | 07-02-2021 5 | 5500 | 05-02-2021
Expected Result
OrderID | PickupItem | PickupDate | DeliveryItem | DeliveryDate
1 | 1100 | 13-02-2021 | 1100 | 13-02-2021
2 | 2200 | 06-02-2021 | Not Delivered | Not Delivered
2 | 2201 | 06-02-2021 | 2201 | 13-02-2021
3 | 3300 | 04-02-2021 | 3300 | 13-02-2021
3 | Not Picked Up | Not Picked Up | 3301 | 13-02-2021
4 | 4400 | 07-02-2021 | Not Delivered | Not Delivered
5 | Not Picked Up | Not Picked Up | 5500 | 13-02-2021
Your database design is poor, as you have no reference point for the existence of a PickupItem. We need to use a nested FULL JOIN to get the results from both tables:
select o.orderid, p.pickupitem, p.date,
d.deliveryitem, d.date
from [order] o
left join
(pickup p
full join delivery d
on d.DeliveryItem = p.pickupitem and d.orderid = p.orderid)
on o.orderid = isnull(d.orderid, p.orderid) ;
This is two left joins, but it is tricky. The second left join needs to refer to both tables -- one for the order id and the other for the item:
select o.orderid, p.pickupitem, p.date,
d.deliveryitem, d.date
from orders o left join
pickups p
on o.orderid = p.orderid left join
delivery d
on o.orderid = d.orderid and p.pickupitem = d.pickupitem;
I'm in need of some assistance to anyone familiar with Oracle SQL. I'm trying to use the Where Not Exists sub query, and is working fine with specific where clauses for specific customers, and then using UNION to join any other customer thereafter. I'm trying to use the SQL in a way where I'm not using UNION to join multiple customers, as there's 100's. I just don't know how to go about it.
I think I need to join the MAIN_ITEM table to the LOCATION table somehow, as it links the items in INVENTORY_LOCATIONS with a zone_code where the location_code can be compared against the table ZONE, showing any mismatches where location_code in INVENTORY_LOCATIONS does not exist in table ZONE. I'm not really sure if I"m explaining this properly, but hopefully my example below clears it up.
Many thanks in advance.
Current Query
select a.company, a.customer, c.customer_name, a.location_code, a.invt1, a.invt2, a.invt3, a.invt_qty
from inventory_locations a left join main_customer c
on a.company=c.company and a.customer=c.customer and a.ware_code=c.ware_code
where not exists (select 1 from zone b where b.location_code = a.location_code and b.zone_code='PM')
and a.company='M1'
and a.customer='100068'
UNION
select a.company, a.customer, c.customer_name, a.location_code, a.invt1, a.invt2, a.invt3, a.invt_qty
from inventory_locations a left join main_customer c
on a.company=c.company and a.customer=c.customer and a.ware_code=c.ware_code
where not exists (select 1 from zone b where b.location_code = a.location_code and b.zone_code='Z1')
and a.company='M1'
and a.customer='100012'
Table 1 - INVENTORY_LOCATIONS A
COMPANY | WARE_CODE |CUSTOMER | LOCATION_CODE |INVT1 | INVT2 | INVT3 | INVT_QTY
--------------------------------------------------------------------------------------
M1 | 01 | 100012 | 0101A |000052 | T100 | 000001001 | 60
M1 | 01 | 100012 | 0602A |000053 | T101 | 000001002 | 60
M1 | 01 | 100068 | 0601A |CANDY | T200 | 000001080 | 25
M1 | 01 | 100068 | 0102A |CANDY2 | T202 | 000001081 | 25
Table 2 - ZONE B
COMPANY | WARE_CODE |ZONE_CODE | LOCATION_CODE
--------------------------------------------------------
M1 | 01 |PM | 0101A
M1 | 01 |PM | 0102A
M1 | 01 |Z1 | 0601A
M1 | 01 |Z1 | 0602A
Table 3 - MAIN_ITEM D
COMPANY | WARE_CODE | CUSTOMER | ITEM_CODE | ZONE_CODE
----------------------------------------------------------------
M1 | 01 | 100012 | 000052 | PM
M1 | 01 | 100012 | 000053 | PM
M1 | 01 | 100068 | CANDY | Z1
M1 | 01 | 100068 | CANDY2 | Z1
Current results with above query.
COMPANY | CUSTOMER | CUSTOMER_NAME | LOCATION_CODE | INVT1 | INVT2 | INVT3 | INVT_QTY
---------------------------------------------------------------------------------------------------
M1 | 100012 | TEST COMP 1 | 0602A | 000053 | T101 | 000001002 | 60
M1 | 100068 | TEST COMP 2 | 0102A | CANDY2 | T202 | 000001081 | 25
Expected results with a query that doesn't use UNION to join multiple customers.
COMPANY | CUSTOMER | CUSTOMER_NAME | LOCATION_CODE | INVT1 | INVT2 | INVT3 | INVT_QTY
-------------------------------------------------------------------------------------------------
M1 | 100012 | TEST COMP 1 | 0602A | 000053 | T101 | 000001002 | 60
M1 | 100068 | TEST COMP 2 | 0102A | CANDY2 | T202 | 000001081 | 25
Thank you for taking the time to read and assist. Greatly appreciated.
I don't fully understand the semantics of your tables and am not sure of the primary keys which means the join conditions could need to be corrected.
However, my interpretation of your goal is to find which inventory_location rows imply a combination of location code and zone that is not in the zone table.
So I would do as follows:
Take the inventory_location table, and add on the customer_name and zone_code with joins. I am assuming each row has only one customer and only one zone. A "with" clause is convenient to treat this as if it were a single table.
Then take the location and zone code combinations and see which ones are missing from the zone table with a "where not exists" clause.
I apologize in advance for any typos/syntax errors. Without actually executing it, I think it would produce your requested output.
with inv_loc as (
select a.company, a.customer, c.customer_name, a.location_code, a.invt1, a.invt2, a.invt3, a.invt_qty, d.zone_code
from inventory_locations a
left join main_customer c on a.company=c.company and a.customer=c.customer and a.ware_code=c.ware_code
left join main_item d on d.company = a.company and d.customer = a.customer and d.ware_code = a.ware_code and d.item_code = a.invt1
)
select
company, customer, customer_name, location_code, invt1, invt2, invt3, invt_qty
from inv_loc i
where not exists (
select 1 from zone b
where b.location_code = i.location_code and b.zone_code =i.zone_code
)
I have a table 2 transactions let's say table A and B, for some cases i need to transfer row datas from table B to A as new table with several conditions :
The price for the data transferred will follow from the previous data
The same date will not processed into results
When there is no previous data, it will not processed into results
For Example :
-----------Table A------------- ----------Table B----------
product | Date | Price | | Product | Date |
A | 2019-01-01 | 10 | | A | 2018-11-05 |
A | 2019-01-15 | 15 | | A | 2019-01-10 |
A | 2019-01-25 | 20 | | A | 2019-01-12 |
A | 2019-05-01 | 25 | | A | 2019-01-27 |
A | 2019-07-02 | 30 | | B | 2019-02-10 |
B | 2019-02-05 | 40 | | B | 2019-04-22 |
B | 2019-04-22 | 50 | | B | 2019-05-13 |
B | 2019-05-12 | 40 |
Result :
-----------Table C-------------
product | Date | Price |
A | 2019-01-01 | 10 |
A | 2019-01-10 | 10 | *The prices follow the data in the previous date (2019-01-01)
A | 2019-01-12 | 10 | *The prices follow the data in the previous date (2019-01-01)
A | 2019-01-15 | 15 |
A | 2019-01-25 | 20 |
A | 2019-01-27 | 20 | *The prices follow the data in the previous date (2019-01-25)
A | 2019-05-01 | 25 |
A | 2019-07-02 | 30 |
B | 2019-02-05 | 40 |
B | 2019-02-10 | 40 | *The prices follow the data in the previous date (2019-02-05)
B | 2019-04-22 | 50 |
B | 2019-05-12 | 40 |
B | 2019-05-13 | 40 | *The prices follow the data in the previous date (2019-05-12)
NOTE:
For product A in Table B on 2018-11-05 not processed into results because there's no data before that date in the table A for that product.
For product B in Table B on 2019-04-22 not processed into results because the date and product in table A and B are the same (The data is already in table A)
I try not to use looping mechanism because my data reaches millions, but i was too dizzy to think about it.
One way is using group by in a cte and then union:
WITH cte AS(
SELECT b.product,
b.[Date],
MAX(a.[Date]) AS [DateValue]
FROM TableA AS a
INNER JOIN TableB AS b ON a.product = b.product
WHERE a.[Date] <= b.[Date]
GROUP BY b.product, b.[Date]
)
SELECT *
FROM dbo.TableA AS a
UNION
SELECT b.product,
b.[Date],
a.Price
FROM cte AS c
INNER JOIN dbo.TableB AS b ON b.product = c.product AND b.[Date] = c.[Date]
INNER JOIN dbo.TableA AS a ON a.product = c.product AND a.[Date] = c.[DateValue]
ORDER BY product, [Date]
One method uses union all and cross apply:
select ab.product, ab.date, p.price
from ((select a.product, a.date
from a
) union -- intentional to remove duplicates
(select b.product b.date
from b
)
) ab cross apply
(select top (1) a.price
from a
where a.product = ab.product and a.date <= ab.date
order by ab.date desc
) p;
Note that cross apply will eliminate the rows from b that have no price.
If SQL support the ignore nulls option on either last_value() or lag(), this would be more appropriate with a full join:
select coalesce(a.product, b.product) as product,
coalesce(a.date, b.date) as date,
coalesce(a.price,
lag(ignore nulls a.price) over (partition by coalesce(a.product, b.product) order by coalesce(a.date, b.date)) as price
from a full join
b
on a.product = b.product and a.date = b.date;
Alas, SQL Server does not (currently) support that. You can make that work with a bit of effort and additional subqueries.
SQL MERGE is a very powerful tool to perform "CRUD" operation based on some condition...
Please follow the link for more details of this feature.
http://www.sqlservertutorial.net/sql-server-basics/sql-server-merge/
https://www.essentialsql.com/introduction-merge-statement/
Please feel free to ask if you have any doubt.
I need help with this.
Tbl: WarehouseInventory
Date | DelRec | ProductId | Quantity
2015-09-10 | 110 | 1 | 100
2015-09-12 | 111 | 1 | 100
2015-09-12 | 111 | 2 | 200
2015-09-12 | 111 | 3 | 300
Tbl: Withdrawals
Date | ID | ProductId | Quantity | CustomerId
2015-09-11 | 1 | 1 | 400 | 2
2015-09-12 | 1 | 1 | 100 | 1
2015-09-12 | 2 | 2 | 200 | 1
2015-09-12 | 3 | 3 | 300 | 1
Tbl: Customers
Customer Id | Name
1 | Somebody
2 | Someone
The output should be like this
DelRec | Date Added | ProductId | Stocked | Withdrawn | Customer
110 | 2015-09-10 | 1 | 100 | 0 | NULL
0 | 2015-09-11 | 1 | 0 | 400 | Someone
111 | 2015-09-12 | 1 | 100 | 100 | Somebody
111 | 2015-09-12 | 2 | 200 | 200 | Somebody
111 | 2015-09-12 | 3 | 300 | 300 | Somebody
This is what I have come up so far and it's giving me a wrong output
select wi.DateAdded as 'Date Added', max(wi.DeliveryReceipt) as 'Delivery Receipt', wi.ProductId as 'Product',
max(isnull(wi.Quantity, 0)) as 'Stocked', max(isnull(w.Quantity, 0)) as 'Withdrawn', e.Customers as 'Customer'
from WarehouseInventory wi
cross join Withdrawals w
cross join Customer e
group by wi.DateAdded, wi.ProductId, e.Customers, wi.DeliveryReceipt, w.ProductId
Basically, I need to join the two tables on the date and product and if there is a null value in one of the tables, just make it 0. I appreciate your help.
You can use a FULL OUTER JOIN:
SELECT DelRec,
COALESCE(wi.[Date], wd.[Date]) AS Date_Added,
COALESCE(wi.ProductId, wd.ProductId) AS ProductId,
COALESCE(wi.Quantity, 0) AS Stocked,
COALESCE(wd.Quantity, 0) AS Withdrawn,
c.Name AS Customer
FROM WarehouseInventory AS wi
FULL OUTER JOIN Withdrawals AS wd
ON wi.[Date] = wd.[Date] AND wi.ProductId = wd.ProductId
LEFT JOIN Customers AS c ON c.[Customer Id] = wd.CustomerId
ORDER BY Date_Added
You have a few inconsistencies between your example table and your query, but here's the basic gist:
You want to FULL OUTER JOIN your Warehouse delivery (A) and Withdrawal (B) tables on both product and date
Make sure you coalesce(A, B) for both date and product
Sum the quantities from each table, then coalesce outside each aggregate to get zeros (since one column can be all nulls).
Here:
select
coalesce(wi.DateAdded, w.date) as 'Date Added',
max(wi.DeliveryReceipt) as 'Delivery Receipt',
coalesce(wi.ProductId, w.productId) as 'Product',
coalesce(sum(wi.Quantity), 0) as 'Stocked',
coalesce(sum(w.Quantity), 0) as 'Withdrawn',
e.name as 'Customer'
from WarehouseInventory wi
full outer join Withdrawals w on w.date = wi.dateadded and w.productId = wi.productId
left join Customer e on e.customerId = w.customerId
group by
coalesce(wi.DateAdded, w.date),
coalesce(wi.ProductId, w.productId),
e.name
I am trying to pull some data with transaction counts, by branch, by week, which will later be used to feed some dynamic .Net charts.
I have a calendar table, I have a branch table and I have a transaction table.
Here is my DB info (only relevant columns included):
Branch Table:
ID (int), Branch (varchar)
Calendar Table:
Date (datetime), WeekOfYear(int)
Transaction Table:
Date (datetime), Branch (int), TransactionCount(int)
So, I want to do something like the following:
Select b.Branch, c.WeekOfYear, sum(TransactionCount)
FROM BranchTable b
LEFT OUTER JOIN TransactionTable t
on t.Branch = b.ID
JOIN Calendar c
on t.Date = c.Date
WHERE YEAR(c.Date) = #Year // (SP accepts this parameter)
GROUP BY b.Branch, c.WeekOfYear
Now, this works EXCEPT when a branch doesn't have any transactions for a week, in which case NO RECORD is returned for that branch on that week. What I WANT is to get that branch, that week and "0" for the sum. I tried isnull(sum(TransactionCount), 0) - but that didn't work, either. So I will get the following (making up sums for illustration purposes):
+--------+------------+-----+
| Branch | WeekOfYear | Sum |
+--------+------------+-----+
| 1 | 1 | 25 |
| 2 | 1 | 37 |
| 3 | 1 | 19 |
| 4 | 1 | 0 | //THIS RECORD DOES NOT GET RETURNED, BUT I NEED IT!
| 1 | 2 | 64 |
| 2 | 2 | 34 |
| 3 | 2 | 53 |
| 4 | 2 | 11 |
+--------+------------+-----+
So, why doesn't the left-outer join work? Isn't that supposed to
Any help will be greatly appreciated. Thank you!
EDIT: SAMPLE TABLE DATA:
Branch Table:
+----+---------------+
| ID | Branch |
+----+---------------+
| 1 | First Branch |
| 2 | Second Branch |
| 3 | Third Branch |
| 4 | Fourth Branch |
+----+---------------+
Calendar Table:
+------------+------------+
| Date | WeekOfYear |
+------------+------------+
| 01/01/2015 | 1 |
| 01/02/2015 | 1 |
+------------+------------+
Transaction Table
+------------+--------+--------------+
| Date | Branch | Transactions |
+------------+--------+--------------+
| 01/01/2015 | 1 | 12 |
| 01/01/2015 | 1 | 9 |
| 01/01/2015 | 2 | 4 |
| 01/01/2015 | 2 | 2 |
| 01/01/2015 | 2 | 23 |
| 01/01/2015 | 3 | 42 |
| 01/01/2015 | 3 | 19 |
| 01/01/2015 | 3 | 7 |
+------------+--------+--------------+
If you want to return a query that contains each Branch and each week, then you'll need to first create a full list of that, then use a LEFT JOIN to the transactions to get the count. The code will be similar to:
select bc.Branch,
bc.WeekOfYear,
TotalTransaction = coalesce(sum(t.TransactionCount), 0)
from
(
select b.id, b.branch, c.WeekOfYear, c.date
from branch b
cross join Calendar c
-- if you want to limit the number of rows returned use a WHERE to limit the weeks
-- so far in the year or using the date column
WHERE c.date <= getdate()
and YEAR(c.Date) = #Year // (SP accepts this parameter)
) bc
left join TransactionTable t
on t.Date = bc.Date
and bc.id = t.branch
GROUP BY bc.Branch, bc.WeekOfYear
See Demo
This code will create in your subquery a full list of each branch with each date. Once you have this list, then you can JOIN to the transactions to get your total transaction count and you'd return each date as you want.
Bring in the Calendar before you bring in the transactions:
SELECT b.Branch, c.WeekOfYear, sum(TransactionCount)
FROM BranchTable b
INNER JOIN CalendarTable c ON YEAR(c.Date) = #Year
LEFT JOIN TransactionTable t ON t.Branch = b.ID AND t.Date = c.Date
GROUP BY b.Branch, c.WeekOfYear
ORDER BY c.WeekOfYear, b.Branch