SQL Multiple INNER JOINS In One Select-Statement date-wise - sql

I am using this code for inventory management system, in which i want to retrieve stock in hand date-wise from salestb using Three tables
Table Schema
Productmastertb
prod_id,
Product_name
salesdetailstb
sales_id,
Prod_id,
Prod_qty,
billno
salestb
billno
billdate
I need Result something like this
--------------------------------
Product ID | Product Name | Qty
--------------------------------
1 Mouse 10
2 Keyboard 60
3 Headphone 30
---------------------------------

Hope you will find like below solution (date wise qty):
SELECT pm.product_id
,pm.Product_Name
,SUM(sd.Prod_qty) as Qty
,s.billdate
FROM Productmastertb pm
JOIN salesdetailstb as sd ON sd.product_id = pm.product_id
JOIN salestb as s ON sd.billno = s.billno
GROUP BY pm.product_id, pm.Product_Name, s.billdate
Hope it would be helpful to you !
If its not as per your expectation, kindly provide sample output result with partition by date.

Related

Sql query to calculate first occurrence of a sales order not fulfilled by stock

I have two tables:
Sales Orders (SO ) with fields:Part, Due_Date, Qty
Part with fields Part and Stock.
I an trying to write a query that will produce the first occurrence ( by date - SO.Due_Date) that a sales order (SO.Qty) cannot be fulfilled by the stock.
This is easy if there is no stock i.e. Part.Stock=0 or if there is only one sales order for the part (SO.Qty > Part.Stock)
If there are multiple sales orders I only want the first one shown e.g.
Part.Part = Box , Part.Stock = 250
SO.Part | SO.Due_Date | SO.Qty
Box | 26/10/2014 | 100
Box | 27/10/2014 | 100
Box | 28/10/2014 | 100 * Return this row
Box | 29/10/2014 | 100
I think I need a sub query or need to use CTE but I can't work it out unless I use a loop. The tables have thousands of parts and sales orders and I am trying to run this query as quickly as possible.
Many thanks for your help
I assume this is a learning exercise, as no real business would work this way.
Anyway, here is a query to do what you want:
select *
from sales_order as so1
where due_date =
(select min(due_date)
from sales_order as so2
inner join part as p on p.part = so2.part
where so1.part = so2.part
and stock < (
select sum(quantity)
from sales_order as so3
where so3.due_date <= so2.due_date
and so3.part = so2.part
)
)
Which I have put into a working fiddle here: http://sqlfiddle.com/#!2/bd8ab5/1
There are some assumptions such as one order per date, but I believe it answers the question.
A query that uses a self join to calculate the running quantity total for each row and selects the row with the smallest due date having a running total greater than p.stock
select so.part, so.due_date, so.quantity
from sales_order so
join part p on p.part = so.part
join sales_order so2 on so2.part = so.part
and so2.due_date <= so.due_date
where p.part = 'Box'
group by so.part, so.due_date, so.quantity
having sum(so2.quantity) > max(p.stock)
order by so.due_date limit 1

SQL Inner Join query

I have following table structures,
cust_info
cust_id
cust_name
bill_info
bill_id
cust_id
bill_amount
bill_date
paid_info
paid_id
bill_id
paid_amount
paid_date
Now my output should display records (1 jan 2013 to 1 feb 2013) between two bill_dates dates as single row as follows,
cust_name | bill_id | bill_amount | tpaid_amount | bill_date | balance
where tpaid_amount is total paid for particular bill_id
For example,
for bill id abcd, bill_amount is 10000 and user pays 2000 one time and 3000 second time
means, paid_info table contains two entries for same bill_id
bill_id | paid_amount
abcd 2000
abcd 3000
so, tpaid_amount = 2000 + 3000 = 5000 and balance = 10000 - tpaid_amount = 10000 - 5000 = 5000
Is there any way to do this with single query (inner joins)?
You'd want to join the 3 tables, then group them by bill ids and other relevant data, like so.
-- the select line, as well as getting your columns to display, is where you'll work
-- out your computed columns, or what are called aggregate functions, such as tpaid and balance
SELECT c.cust_name, p.bill_id, b.bill_amount, SUM(p.paid_amount) AS tpaid, b.bill_date, b.bill_amount - SUM(p.paid_amount) AS balance
-- joining up the 3 tables here on the id columns that point to the other tables
FROM cust_info c INNER JOIN bill_info b ON c.cust_id = b.cust_id
INNER JOIN paid_info p ON p.bill_id = b.bill_id
-- between pretty much does what it says
WHERE b.bill_date BETWEEN '2013-01-01' AND '2013-02-01'
-- in group by, we not only need to join rows together based on which bill they're for
-- (bill_id), but also any column we want to select in SELECT.
GROUP BY c.cust_name, p.bill_id, b.bill_amount, b.bill_date
A quick overview of group by: It will take your result set and smoosh rows together, based on where they have the same data in the columns you give it. Since each bill will have the same customer name, amount, date, etc, we are fine to group by those as well as the bill id, and we'll get a record for each bill. If we wanted to group it by p.paid_amount, though, since each payment would have a different one of those (possibly), you'd get a record for each payment as opposed to for each bill, which isn't what you'd want. Once group by has smooshed these rows together, you can run aggregate functions such as SUM(column). In this example, SUM(p.paid_amount) totals up all the payments that have that bill_id to work out how much has been paid. For more information, please look at W3Schools chapter on group by in their SQL tutorials.
Hope I've understood this correctly and that this helps you.
This will do the trick;
select
cust_name,
bill_id,
bill_amount,
sum(paid_amount),
bill_date,
bill_amount - sum(paid_amount)
from
cust_info
left outer join bill_info
left outer join paid_info
on bill_info.bill_id=paid_info.bill_id
on cust_info.cust_id=bill_info.cust_id
where
bill_info.bill_date between X and Y
group by
cust_name,
bill_id,
bill_amount,
bill_date

How to select data based on two tables

I have two tables and want to select only the ones where a certain status is 'complete' in the second table. The most important factor is that all shipments need to be completed.
First Table (orders) includes a unique order id and several cells with customer details. For example
Order_id | Name
1001 | John
1002 | Paula
1003 | Ben
The second table (shipments) has all the items a customer ordered and the status whether they were delivered e.g.
Order_id | Shipment_number | Status
1001 | 8004 | complete
1001 | 8003 | processing
1002 | 8005 | complete
1003 | 8008 | processing
1003 | 8007 | processing
1003 | 8009 | complete
I tried it with the following code but unfortunately the results show all order ids where at least one of the associated shipments is 'complete'.
SELECT
order_id,
name
FROM orders
INNER JOIN shipments ON orders.order_id = shipments.order_id
WHERE
shipments.status = 'complete';
I'm pretty new to SQL and really struggling with this. Thanks in advance :)
SELECT Orders.OrderID
FROM orders
WHERE NOT EXISTS (SELECT OrderID
FROM shipments
WHERE status != 'complete' AND
shipments.OrderID = Orders.OrderID)
You can use nested queries to do this:
Write a query to take a count of the shipments.order_id group by order_id, this gives you the total shipments for each order, select the order_id and the count.
Write a query to take a count of the shipments.order_id group by order_id where shipments.status = 'complete', this gives you the completed shipments for each order, select the order_id and the count.
join the result of 1 and 2 with order on order id where (1.count = 2.count), select the name and the order_id.
You could try this
SELECT order_id FROM <orders>
WHERE order_id NOT IN (SELECT order_id FROM <shipments> WHERE status <> 'complete')
Even though, depending on your system I would consider adding a field in order table (effectively de-normalizing it, but benefits/drawbacks depend on how often you need this information) and update it with current order status.
If you need this information often, I might be worth it.
SELECT ship.order_id, ord.name
FROM shipments as ship
left join orders as ord on
ord.order_id = ship.order_id
where ship.status = 'complete'
Generally, the best way to do this is to have a level at the order itself that indicates whether or not the order is completely shipped.
But, this would work although it might not scale really well. To get it to scale better, you would have to do another join BACK to orders in the subqueries to limit the orders that you are looking through.
select order_id, name from
orders JOIN
(select order_id, count(*) from shipments where status = 'complete' group by order_id
INTERSECT
select order_id, count(*) from shipments group by order_id)
on order_id

How to Select and Order By columns not in Groupy By SQL statement - Oracle

I have the following statement:
SELECT
IMPORTID,Region,RefObligor,SUM(NOTIONAL) AS SUM_NOTIONAL
From
Positions
Where
ID = :importID
GROUP BY
IMPORTID, Region,RefObligor
Order BY
IMPORTID, Region,RefObligor
There exists some extra columns in table Positions that I want as output for "display data" but I don't want in the group by statement.
These are Site, Desk
Final output would have the following columns:
IMPORTID,Region,Site,Desk,RefObligor,SUM(NOTIONAL) AS SUM_NOTIONAL
Ideally I'd want the data sorted like:
Order BY
IMPORTID,Region,Site,Desk,RefObligor
How to achieve this?
It does not make sense to include columns that are not part of the GROUP BY clause. Consider if you have a MIN(X), MAX(Y) in the SELECT clause, which row should other columns (not grouped) come from?
If your Oracle version is recent enough, you can use SUM - OVER() to show the SUM (grouped) against every data row.
SELECT
IMPORTID,Site,Desk,Region,RefObligor,
SUM(NOTIONAL) OVER(PARTITION BY IMPORTID, Region,RefObligor) AS SUM_NOTIONAL
From
Positions
Where
ID = :importID
Order BY
IMPORTID,Region,Site,Desk,RefObligor
Alternatively, you need to make an aggregate out of the Site, Desk columns
SELECT
IMPORTID,Region,Min(Site) Site, Min(Desk) Desk,RefObligor,SUM(NOTIONAL) AS SUM_NOTIONAL
From
Positions
Where
ID = :importID
GROUP BY
IMPORTID, Region,RefObligor
Order BY
IMPORTID, Region,Min(Site),Min(Desk),RefObligor
I believe this is
select
IMPORTID,
Region,
Site,
Desk,
RefObligor,
Sum(Sum(Notional)) over (partition by IMPORTID, Region, RefObligor)
from
Positions
group by
IMPORTID, Region, Site, Desk, RefObligor
order by
IMPORTID, Region, RefObligor, Site, Desk;
... but it's hard to tell without further information and/or test data.
A great blog post that covers this dilemma in detail is here:
http://bernardoamc.github.io/sql/2015/05/04/group-by-non-aggregate-columns/
Here are some snippets of it:
Given:
CREATE TABLE games (
game_id serial PRIMARY KEY,
name VARCHAR,
price BIGINT,
released_at DATE,
publisher TEXT
);
INSERT INTO games (name, price, released_at, publisher) VALUES
('Metal Slug Defense', 30, '2015-05-01', 'SNK Playmore'),
('Project Druid', 20, '2015-05-01', 'shortcircuit'),
('Chroma Squad', 40, '2015-04-30', 'Behold Studios'),
('Soul Locus', 30, '2015-04-30', 'Fat Loot Games'),
('Subterrain', 40, '2015-04-30', 'Pixellore');
SELECT * FROM games;
game_id | name | price | released_at | publisher
---------+--------------------+-------+-------------+----------------
1 | Metal Slug Defense | 30 | 2015-05-01 | SNK Playmore
2 | Project Druid | 20 | 2015-05-01 | shortcircuit
3 | Chroma Squad | 40 | 2015-04-30 | Behold Studios
4 | Soul Locus | 30 | 2015-04-30 | Fat Loot Games
5 | Subterrain | 40 | 2015-04-30 | Pixellore
(5 rows)
Trying to get something like this:
SELECT released_at, name, publisher, MAX(price) as most_expensive
FROM games
GROUP BY released_at;
But name and publisher are not added due to being ambiguous when aggregating...
Let’s make this clear:
Selecting the MAX(price) does not select the entire row.
The database can’t know and when it can’t give the right answer every
time for a given query it should give us an error, and that’s what it
does!
Ok… Ok… It’s not so simple, what can we do?
Use an inner join to get the additional columns
SELECT g1.name, g1.publisher, g1.price, g1.released_at
FROM games AS g1
INNER JOIN (
SELECT released_at, MAX(price) as price
FROM games
GROUP BY released_at
) AS g2
ON g2.released_at = g1.released_at AND g2.price = g1.price;
Or Use a left outer join to get the additional columns, and then filter by the NULL of a duplicate column...
SELECT g1.name, g1.publisher, g1.price, g2.price, g1.released_at
FROM games AS g1
LEFT OUTER JOIN games AS g2
ON g1.released_at = g2.released_at AND g1.price < g2.price
WHERE g2.price IS NULL;
Hope that helps.

Selecting the latest per group of items [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Retrieving the last record in each group
i have 2 tables products and cost
PRODUCT
ProdCode - PK
ProdName
COST
Effectivedate - PK
RetailCOst
Prodcode
i tried this query:
SELECT a.ProdCOde AS id, MAX(EffectiveDate) AS edate, RetailCOst AS retail
FROM cost a
INNER JOIN product b USING (ProdCode)
WHERE EffectiveDate <= '2009-10-01'
GROUP BY a.ProdCode;
uhm yah its showing the right effectivedate but the cost on that specific effectivedate doesnt match.
so i want to select the latest date with the matching cost per item.
for example the date i selected is '2009-12-25' and the records for 1 item are these:
ProdCode |EffectiveDate| Cost
00010000 | 2009-01-05 | 50
00010000 | 2009-05-25 | 48
00010000 | 2010-07-01 | 40
so in result i should get 00010000|2009-05-25|48 because it is lesser than the date on my query and it is the latest for that item. and then i want to to show on my query the latest costs on each product.
hope to hear from you soon! thanks!
You need to use a subquery here:
SELECT maxdates.ProdCode, maxdates.maxDate, cost.RetailCost as retail
SELECT ProdCode, max(EffectiveDate) as maxDate
FROM cost
WHERE EffectiveDate < '2009-10-01'
GROUP BY ProdCode
) maxdates
LEFT JOIN cost ON (maxdates.ProdCode=cost.ProdCode
AND maxdates.maxDate=cost.EffectiveDate)
Explanation:
The inner SELECT gives a list of all Products and their respective maximum EffectiveDates. The join "glues" the retail cost per data entry to the result.
Alternatively, using the old max concat trick should do the trick.
SELECT
p.ProdCode,
SUBSTRING(MAX(CONCAT(d.EffectiveDate, c.RetailCost)), 1, 10) AS date,
SUBSTRING(MAX(CONCAT(d.EffectiveDate, c.RetailCost)), 10, 100) + 0 AS cost
FROM
product p,
cost c
WHERE
p.ProdCode = c.ProdCode AND
c.EffectiveDate < '2009-10-01'
GROUP BY
p.ProdCode