Joining 2 tables error - sql

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

Related

How to do a 3-table join with aggregate functions and a group by clause

I can't figure out how to do a 3-table join with aggregate functions and a group by clause.
I have 3 tables:
Products
id
...
Sales
id
product_id
sale_price
...
Punches
id
punchable_id
punchable_type
...
I want to get all of the products data together with its total number of sales, views, (each row in punches table represents a view) and the total revenue from a product.
I have the following query:
#products = Product.joins("INNER JOIN sales ON sales.product_id = products.id INNER JOIN punches ON sales.product_id = punches.punchable_id AND punches.punchable_type = 'Product'").group("products.id").select("products.*, SUM(sales.sale_price) as revenue, COUNT(sales.id) as total_sales_count, COUNT(punches.id) as views").distinct
However, this is giving me the wrong data for the aggregate functions. The numbers are very high (I think it's summing and counting the rows a bunch of extra times).
When I do the Inner Join query with the products table and only one other table (either the punches or sales table), the data is fine. It blows up whenever I do the 2 joins in one query.
Does anyone know what's wrong with the above and how to properly write the query?
You should join to punches directly with products.id = punches.punchable_id ..etc not via sales.product_id

Where am I doing wrong in this SQLite query?

Introduction
I have three tables: Table_ItemDetails, Table_Invoices, Table_PartsTracker.
Table_ItemDetails contains the details of the item, Table_Invoices contains information pertaining to item quantity, while Table_PartsTracker shows which item is used in which project and how much quantity is used. Among all these tables, Item_Number is the foreign key column
My Objective:
I need to create a view called View_ItemDetails, that contains the following columns
Item_Number, Item_Name, Item_Description, Total_Qty and Avl_Qty
The total qty is got by adding all the quantities of the item from invoices, while available qty is got by subtracting the items used in project from total qty. I have attached the images of my tables.
To achieve this I have written query like this
CREATE VIEW IF NOT EXISTS View_ItemDetails
AS SELECT
Table_ItemDetails.Item_Number,
Table_ItemDetails.Item_Name,
Table_ItemDetails.Item_Description,
SUM(Table_Invoices.Qty) as Total_Qty,
(SUM(Table_Invoices.Qty) - ifnull(SUM(Table_PartsTracker.Items_Used), 0)) as Avl_Qty
FROM Table_ItemDetails
OUTER LEFT JOIN Table_Invoices USING(Item_Number)
OUTER LEFT JOIN Table_PartsTracker USING(Item_Number)
GROUP BY Table_Invoices.Item_Number
but the result in total qty and available qty is wrong. Can someone point me where I am doing wrong?. Thanks
The problem you are having is because you are doing a cross product between details, invoices, and parts. In other words you are getting every parts row for every invoice row, and summing up all the qty figures for all these combinations.
Since there is not direct relationship between invoice and parts, you need to do the groups independently.
CREATE VIEW IF NOT EXISTS View_ItemDetails AS
SELECT
D.Item_Number,
D.Item_Name,
D.Item_Description,
I.Qty as Total_Qty,
I.Qty - ifnull((P.Items_Used), 0) as Avl_Qty
FROM Table_ItemDetails D
OUTER LEFT JOIN (select Item_Number, sum(qty) as qty from Table_Invoices group by Item_Number) I USING(Item_Number)
OUTER LEFT JOIN (select Item_Number, sum(Items_Used) as Items_Used from Table_PartsTracker group by Item_Number) P USING(Item_Number)

stacking data from two tables with different dates

I have two tables, Sales and Returns. They have CustomerID, ProductCode, Name, SalesDate, SalesWeek, SalesAmount and ReturnsDate, ReturnsWeek, ReturnsAmount. What I really want to do is just join these tables and stack them on top of each other so the client has data in a single report for both sales and returns.
Sales and Returns dates are different but the product code, customer ID and Name can be same for a record in output table. For instance Customer A bought a product last month and returned it next month, so his record can appear in returns table.
To achieve this I have tried using union by selecting all columns between both tables but I am getting a mix of records for sales and returns with no consistency. All I want to do is see Nulls for Customers who have no business with Returns and vice versa. I was thinking Left Join in this case should work but it isn't working. So I am seeing mixed up data in all columns for Sales and Returns amounts. Attached is the picture that has two tables, the output I am seeing and the out put I want to see. Also I am performing Weekly aggregations for Sales and Returns amounts. What is the best and easiest way to achieve this ? I am sorry I may have not structured my question properly but the image might help #NewToSQL
Presumably, a customer can purchase a given product more than once. If so, a full join is the right solution, but you need a bit more logic to ensure non-duplication in the results:
select . . .
from (select s.*,
row_number() over (partition by product_code, customer_id order by (select NULL)) as seqnum
from sales s
) s full join
(select r.*,
row_number() over (partition by product_code, customer_id order by (select NULL)) as seqnum
from returns r
) r
on s.product_code = r.product_code and s.customer_id = r.customer_id;
I am leaving the name out, because I assume it is defined by the customer id.
You need to do a FULL JOIN sales table with returns table on those columns that you mentioned.
Something like :
SELECT *
FROM sales s
FULL JOIN returns r
ON s.product_code = r.product_code
AND s.customerid = r.customer_id

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.

How can I SELECT a result set of data from a single table with one of the columns coming from the SUM of a column in a different table?

So let's say I want 5 columns total in my result set, for example:
Name, Date, Color, Price, TotalSales
Name, Date, Color, and Price are all stored in a single table so I will just get every row from that table.
SELECT * FROM AwesomeStuff
However, TotalSales needs to be calculated from the SUM of values in another table, and the values that needed to be combined must match up with the ID of the row from the AwesomeStuff table.
SELECT SUM(SalePrice) FROM AwesomeSales
Each row in the AwesomeSales table has an ID that matches a single item in the AwesomeStuff table. So I want to add up all the SalePrice values into a single column and match it up with the correct row in the AwesomeStuff query so I get back the single result set.
How does this work? Along with the query, can you explain what SQL is doing in plain english so I can understand how to write this type of SELECT again in the future?
Thanks.
You can perform a GROUP BY operation to group the results per "AwesomeStuff" item:
SELECT
A.Name,
A.Date,
A.Color,
A.Price,
SUM(S.SalePrice) AS TotalSales
FROM
AwesomeStuff A
INNER JOIN
AwesomeSales S
ON
S.AwesomeStuffId = A.Id
GROUP BY
A.Name,
A.Date,
A.Color,
A.Price
select
t.name,
t.date,
t.color,
t.price,
a.TotalSales
from awesomestuff t
inner join
(
select id, sum(saleprice) as TotalSales
from awesomesales
group by id
)a
on t.id = a.id
What this does is calculates the SUM() based on each id. And then with an inner join it creates the relationship for the TotalSales per id to show in the result set.
The SQL to do what you want is:
select AwesomeStuff.Name,
AwesomeStuff.Date,
AwesomeStuff.Color,
AwesomeStuff.Price,
AwesomeStuff.TotalSales,
TotalSales.Price
from AwesomeStuff
join (select AwesomeSales.id,
sum(AwesomeSales.SalePrice) As Price
from AwesomeSales
group by id) As TotalSales
on TotalSales.id = AwesomeStuff.id;
I will update this post after lunch with an English explanation.
English You aggregate the sale prices, taking the sum, grouped by their ID. This is what the inline view (sub-query) does: we include the ID because we need to use it in the join. That is we join your AwesomeStuff table with the inline view of sales (aliased as TotalSales, as we refer to it later), match them by ID -- as this is the primary key -- and then select all the fields (including the calculated sum, which we have aliased as Price) together.
You could use a subquery if your Database supports it:
SELECT stuff.Name, stuff.Date, stuff.Color, stuff.Price, sales.total
FROM AwesomeStuff AS stuff,
(SELECT STUFF_ID, SUM(SalePrice) AS total
FROM AwesomeSales
GROUP BY STUFF_ID) AS sales
where stuff.ID = sales.STUFF_ID
SELECT
a.Name,
a.Date,
a.Color,
a.Price,
SUM(s.SalesPrice)
FROM AwesomeStuff as a
INNER JOIN AwesomeSales as s on a.ID = s.AwesomestuffID
GROUP BY a.Name, a.Date, a.Color, a.Price
In plain English, what you are doing is saying give me all the unique combinations of name, date, color and price from your stuff table and a sum of all the sales from the sales table. This assumes that each ID in the stuff table represents a unique combination of name, date, color and price.
Try:
SELECT A.Id, -- not required in select for query to work - included for clarity
max(A.Name),
max(A.Date),
max(A.Color),
max(A.Price),
SUM(S.SalePrice) AS TotalSales
FROM AwesomeStuff A
LEFT JOIN AwesomeSales S ON S.AwesomeStuffId = A.Id
GROUP BY A.Id
By joining AwesomeStuff to AwesomeSales on the product's unique ID, you will get a row returned for every sale of every product - since you only want one row per product, you need to group by the product's unique ID. (The SUM function should be self-explanatory.)
Unfortunately, most forms of SQL will not allow you to include unaggregated values in a grouped query, even where these are functionally dependant on one of the values that is being grouped - as in this query. This is why name, date, color and price have all been MAXed.
A left join ensures that records on the left-hand side of the join (here, AwesomeStuff) will be returned even if there are no corresponding records on the right-hand side of the join (AwesomeSales). In other words, this version of the query will include products that have had no sales.