SQL Join Query with three tables - sql

I have three tables.
1st table item_stock(item_id,item_name,item_current_qty)
2nd table item_purchase(item_id,item_purchase_qty,item_purchase_date)
3rd table item_transaction(item_id,item_txn_to,item_txn_qty,txn_date)
Item stock table has details of what on hand qty we have of all items, item_purchase table has information of which item was purchase with what quantity and item_transaction table has details of transaction happened for all items.
What I have tried is :
select item_stock.item_name,item_stock.item_current_qty as on_hand,sum(item_purchase.item_purchase_qty) as purchased ,sum(item_transaction.item_txn_qty) as Txnd
from item_stock
full outer join item_purchase on item_purchase.item_id=item_stock.item_id
full outer join item_transaction.item_id=item_stock.item_id
where item_purchase.item_purchase_date between '2022-11-01' and '2022-11-30'
and item_transaction.txn_date between '2022-11-01' and '2022-11-30'
group by item_purchase.item_id,item_transaction.item_id
order by item_stock.item_name asc
Output:
----------------------------------------------
--item_name----on_hand---purchased----Txnd----
-- TN2280 --- 25 --- 6 --- 25 ----
-- TN2590 --- 14 --- 8 --- 26 ----
----------------------------------------------
Output is showing only items which are purchased and transacted.
What I need is:
I need a table output which will display all items of item_stock with pattern like; "item_name","on_hand","purchased","Txnd". I want to put date filter in 2nd and 3rd table.
For example in November 2022 what qty was purchased for each item and how many items were transacted in the same month.

So the problem is you are using full outer joins and then including the joined tables in the where clause -- since where will be false for null items you are not getting results from a full outer join you are getting results for an inner join -- the null items are discarded.
In order to fix this you need to use left joins (probably what you did first) and then put the criteria in the join. With a left join you won't need the group by and you will just get the results you want. Like this:
SELECT item_stock.item_name,
item_stock.item_current_qty as on_hand,
sum(item_purchase.item_purchase_qty) as purchased,
sum(item_transaction.item_txn_qty) as Txnd
FROM item_stock
LEFT JOIN item_purchase ON item_purchase.item_id=item_stock.item_id
AND item_purchase.item_purchase_date between '2022-11-01' and '2022-11-30'
LEFT JOIN item_transaction ON item_transaction.item_id=item_stock.item_id
AND item_transaction.txn_date between '2022-11-01' and '2022-11-30'
GROUP BY item_purchase.item_id, item_transaction.item_id
ORDER BY item_stock.item_name ASC

Related

Select all rows from joined with multiple conditions tables with null values

I want to join two tables, Sales and Budget.
Sales table columns:
| Customer | Period | Sales |
Budget table columns:
| Customer | Period | SaleBudget |
Sales table has data for periods 1, 2, and 3. Budget has data for periods 1-12. When I try to run below query I get only data for periods from Sales table matched with Budget table. But my goal is to get all data from both tables. Could you give me a hint how to change query?
Select s.Customer, b.SaleBudget, s.Sales from Sales s
full outer join Budget b on b.Customer = s.Customer and b.Period = s.Period
When you use left join its join the rows that are same in
b.Customer = s.Customer and b.Period = s.Period
if you want have all of the rows you shouldn't use left join;
The LEFT JOIN keyword returns all records from the left table (table1), and the matching records from the right table (table2);
there is not any way that get some data matching and some data Inconsistency in one shape.

unable to join 3 tables in SQL

I am lost in joining 3 tables.
I have 3 tables:
Table with Events
Table with date range
Table with reservation count for an event on a date
Now I want to create a timeline so that for every event I have a date range with for every day a count of reservations (or 0 if there are none).
See the SQL Fiddle example
Now the issue is that I can select all records from date range and events but I cannot join the reservation table together with it to select the count of reservation on that day. I need it to create a graph of last x days for an event with the reservationcount on those days.
Expected output would be that for every date (2017-01-01,2017-01-02) I have a list of events(1,2,3..) and the reservationcount on that day (or if there is no matching reservationcount record, 0)
How to do that?
2017-01-01 1 0
2017-01-02 1 65
2017-01-03 1 345
2017-01-04 1 0
2017-01-05 1 0
2017-01-06 1 0
In your sample example there is no direct link between Events and EventDate, so if you want to attach all your events to every available date you need to have a Cartesian Product (cross join) between these tables.
There may or may not be a reservation on a specific date and event, so you need a LEFT JOIN with the reservation table.
Following query should give you the desired output.
SELECT SelDate,EV.EventId, ISNULL(ResCount,0) as ResCount FROM
EventDates ED
INNER JOIN Events EV ON 1=1
LEFT JOIN Reservations RES ON ED.SelDate = RES.EventDate AND EV.EventId=RES.EventId
Or you can also write it as
SELECT ED.*, ISNULL(ResCount,0) as ResCount FROM
(
SELECT SelDate, EventId
FROM EventDates ,Events
) ED
LEFT JOIN Reservations RES ON ED.SelDate = RES.EventDate AND ED.EventId=RES.EventId
You were close, but used the wrong outer join and missed a condition.
Select EventDates.SelDate,Events.EventID
,coalesce(Reservations.ResCount,0)
from EventDates cross join Events -- one row for each event/date
left Outer Join Reservations -- join existing reservations
on Events.EventID=Reservations.EventID
and EventDates.SelDate = Reservations.EventDate
order by Events.EventID, EventDates.SelDate
See fiddle

Transact-SQL Forcing a JOIN?

I'm trying to create a report with a static number of rows.
Table #1 Field #1 is a list of all Items we currently sell.
Table #2 Field #1 we have a list of all Locations that we sell from.
Table #3 contains all sales date including Item Numbers and Location Codes
I currently have a select to give me a list of all Distinct Item Numbers (Lets say 1000 of them)
Before I left Join the sales data from Table #3 I want to find a way to increase my number of rows and make a distinct row for each location for every item.
I.e. if I have a 1000 items and 10 locations I want to have 10,000 rows so that I can left join sales date ON Item Number & Location Code. I want it this way so I will have a row even if there is no sales data.
Since we havent sold every item in every location a left join from the sales table will not accomplish this. **Also the Table with Locations has no common fields with Item table to join ON. Is there a different kind of JOIN or a different SQL function to accomplish this?
Thanks!
You would use cross join and left join. For instance, to sum the sales:
select i.itemid, l.locationid, sum(t.sales)
from items i cross join
locations l left join
table3 t
on i.itemid = t.itemid and l.locationid = t.locationid
group by i.itemid, l.locationid
order by i.itemid, l.locationid;
If you only have one row in table3, then the aggregation is not necessary.

Joining 2 tables error

I'm trying to join 2 tables to get an output report. The tables involved are the stock and dailysales table.
Stock and Dailysales tables:
Desired output format:
I am trying to join 2 tables by using the below query
Select item,article,sold,stockonhand
from stock S
left join dailysales as D on S.item=D.item
group by item
I want the output to include all rows from the stock table. Like there is stock, but not sold, also to be included in the report. Currently my report is does not show, (stocked but not sold).
Hope you can understand my context or point me to a tutorial which I could read up. I tried to search for few days and couldn't find an exact question to mine.
Not tested -
select item,article,sum(sold),sum(stockonhand)-sum(sold) from (
select a.item,a.article,a.stockonhand,case when b.sold is null then 0 else b.sold end as sold
from stock a left join dailysales b on (a.item = b.item))
group by item,article;
It's basically does the left join and put 0 on the null column(for sum after)
and then summing up the results grouping by all the columns in the select(that what was wrong with your query)
Simply LEFT JOIN the two tables (to get the flute too), do a GROUP BY, and SUM the sold:
select s.item, s.article, coalesce(SUM(d.sold),0) as "qty sold", s.stockonhand
from stock S
left join dailysales as D on S.item=D.item
group by s.item, s.article, s.stockonhand
The coalesce is there to replace NULL with 0, for items not sold. (Thanks sagi!)
General GROUP BY tip: If a GROUP BY clause is specified, each column reference in the SELECT list must either identify a grouping column or be the argument of a set function.
Also, you can remove the article column from the dailysales table. That data is already stored in the stock table. Never store same data twice! (Normalization!) Risk for data inconsistency if you keep that column.
You can sum the item in a separated table, it would be clearer than GROUP BY 2 tables
SELECT
s.item,
s.article,
ISNULL(ds.sold, 0) AS qtysold,
s.stockonhand
FROM
stock s OUTER APPLY
(SELECT SUM(sold) AS sold FROM dailysales WHERE item = s.item GROUP BY item) ds

WHERE using a temporary table

I have three tables: customers, orders and refunds. Some customers (not all) placed orders and for some orders (not all) there were refunds.
When I join the three tables like this (the details are not that important):
SELECT ...
FROM customers LEFT JOIN orders
ON customers.customer_id=orders.customer_id
LEFT JOIN refunds
ON orders.order_id=refunds.order_id;
//WHERE order_id IS NOT NULL;// uncomment to filter out customers that have no orders
I get a big table in which all customers are listed (even the ones that have not placed any orders and they have NULL in the 'order_id' column), with all their orders and the orders' refunds (even if not all orders have refunds):
NAME ORDER_ID ORDER AMOUNT REFUND
------------------------------------------------------------
Natalie 2 12.50 NULL
Natalie 3 18.00 18.00
Brenda 4 20.00 NULL
Adam NULL NULL NULL
Since I only want to see only customers that have placed orders, i.e in this case I want to filter Adam from the table, I uncomment the 'WHERE' row from the SQL query above.
This yields the desired result.
My question is:
On which table is the WHERE executed - on the original 'orders' table (which has no order_id that is NULL) or on the table that is result of the JOINs?
Apparently it is the latter, but just want to make sure, since it is not very obvious from the SQL syntax and it is a very important point.
Thank you
In this case, you're making SQL work harder than it has to. It is operating on the results (likely a MERGE event, or something along those lines).
There's a chance SQL is realizing what you're doing and optimizing the plan and changing to an INNER JOIN for you. But I can't be certain (and neither can SQL -- it can change how it optimizes over time).
In the case where you only want where an order is there, use an INNER JOIN instead. SQL will be much more efficient at this.
SELECT ...
FROM customers
INNER JOIN orders
ON customers.customer_id=orders.customer_id
LEFT JOIN refunds
ON orders.order_id=refunds.order_id;
You can change the LEFT JOIN as INNER JOIN to eliminate customers which don't have any order
SELECT ...
FROM customers INNER JOIN orders
ON customers.customer_id=orders.customer_id
LEFT JOIN refunds
ON orders.order_id=refunds.order_id;
It's because you're using LEFT JOIN, which will return all rows from the left hand table, in your case this is the Customer Table, and return NULL where no corresponding values appear in the right hand tables.
Just rewrite it using inner joins, so only rows where matching data is found will be returned.
SELECT ...
FROM customers
INNER JOIN orders
ON customers.customer_id=orders.customer_id
INNER JOIN refunds
ON orders.order_id=refunds.order_id;