Joining 3 tables in SQL showing all 3 tables - sql

I am a basic user of SQL but need to join 3 tables together to sow a) sales b) returns and c) profit
I currently have the following code
select * from (
select SUM(Return_Amount) , 'Return' as type, monthname(Return_Date) as month_
from returns
group by month_
union
select SUM(Order_Total_Cost) , 'Sales' as type, monthname(Order_Date) as month_
from sales
group by month_
union
select SUM(profit) as profit_ , 'Profit' as type, month_
from(
select sell_price-cost_price as profit , monthname(order_date) month_
from sales
join order_item
on order_item.order_No = sales.order_No
join returns
on returns.order_no = sales.order_No
join supplier
on supplier.Product_ID = order_item.Product_ID
) B group by month_
) A order by month_;
This is showing as below:
387 Return August
182 Sales August
867 Profit August
733 Return July
109 Sales July
646 Profit July
596 Return June
I want it to show with Return, Sales & Profit as separate columns instead of all types listed in one.
Any help would be greatly appreciated.
Thanks

Why not join them?
SELECT x.month_
, x.returns_
, y.sales_
, z.profit_
FROM
(SELECT SUM(Return_Amount) AS returns_
, monthname(Return_Date) AS month_
FROM RETURNS
GROUP BY month_) x
INNER JOIN
(SELECT SUM(Order_Total_Cost) AS sales_
, monthname(Order_Date) AS month_
FROM sales
GROUP BY month_) y ON x.month_ = y.month_
INNER JOIN
(SELECT SUM(profit) AS profit_
, month_
FROM
(SELECT sell_price-cost_price AS profit
, monthname(order_date) month_
FROM sales
INNER JOIN order_item ON order_item.order_No = sales.order_No
INNER JOIN RETURNS ON returns.order_no = sales.order_No
INNER JOIN supplier ON supplier.Product_ID = order_item.Product_ID) B
GROUP BY month_) z ON x.month_ = z.month_

You could turn the three unioned queries to subqueries and join them. This assumes that each subquery always produces one record per month.
Also: you probably want to join and month and year, in case your data spans over multiple years (which eventually happens in any live dataset). I would also recommend using numeric years and month (as returned by year() and month()), which would produce more efficient joins than month names.
select
r.month_name,
r.total_return_amount,
s.total_sales,
p.total_profit
from
(
select
year(return_date) yr,
month(return_date) mh,
monthname(return_date) month_name
sum(return_amount) total_return_amount,
from returns
group by
year(return_date),
month(return_date),
monthname(return_date)
) r
inner join (
select
year(order_date) yr,
month(order_date) mh,
sum(order_total_cost) total_sales
from sales
group by
year(order_date),
month(order_date)
) s on s.yr = r.yr and s.mh = r.mh
inner join (
select
year(order_date) yr,
month(order_date) mh,
sum(sell_price - cost_price) as total_profit ,
from sales
inner join order_item on order_item.order_no = sales.order_no
inner join returns on returns.order_no = sales.order_no
inner join supplier on supplier.product_id = order_item.product_id
group by
year(order_date),
month(order_date)
) p on p.yr = r.yr and p.mh = r.mh

Related

SQL -percent calculation

Make a report on the sales in 2015 of the products by categories (total value and
quantity sold). Also determine what% of the value of sales
for a given category represent the sales of each of the products in the category.
My query so far:
WITH sales AS
(SELECT t1.category_name
, t2.product_name
, (t3.unit_price*t3.quantity) Total_sales
, EXTRACT (YEAR FROM order_date) Year
FROM categories t1
INNER JOIN
products t2
ON t2.category_id=t1.category_id
INNER JOIN
order_details t3
ON t3.product_id=t2.product_id
INNER JOIN
orders t4
ON t4.order_id=t3.order_id
WHERE EXTRACT (YEAR FROM order_date) = '2015'
GROUP BY t1.category_name
, t2.product_name
, (t3.unit_price*t3.quantity)
, EXTRACT (YEAR FROM order_date)
ORDER BY 1
)
SELECT s.category_name
, s.product_name
, SUM (Total_sales)
FROM sales s
GROUP BY s.category_name
, s.product_name
ORDER BY 1
How to calculate %? Thank you
I think that you want window functions - if your database, that you did not specify, supports them:
SELECT
c.category_name
p.product_name
SUM(od.unit_price * od.quantity) as total_sales
1.0 * SUM(od.unit_price * od.quantity)
/ SUM(SUM(od.unit_price * od.quantity)) OVER(PARTITION BY c.category_id)
as category_sales_ratio
FROM categories c
INNER JOIN products t2 p ON p.category_id = c.ategory_id
INNER JOIN order_details od ON od.product_id = p.product_id
INNER JOIN orders o ON o.order_id = od.order_id
WHERE o.order_date >= '2015-01-01' AND o.order_date < '2016-01-01'
GROUP BY c.category_id, c.ategory_name, p.product_id, p.product_name
ORDER BY c.category_name, p.product_name
The window sum computes the total sales for the whole category, that you can divide the sales of the current product with.
Note that I changed your query in serveral ways:
meaningful table aliases make the query easier to write, read and maintain
filtering dates without transformation is much more efficient that using date functions
there is no need for a subquery
it is always a good idea to put the relevant primary keys in the GROUP BY clause (in case two different products or categories have the same name) - on the other hand, you also had additiona uneeded columns in that clause

Select Sold and unsold product from same table in SQL Server for last month

I have Database table and trying to write query to find sold and not sold product list from one table.
Table is Below
Expecting Result
How do i get this result? i am using CTE to create Tamp table and with all services and then do left join but it dose give me only product sold in Feb, but i want all product with no sell too.
You can cross join the products and the dates, and then bring the table with a left join:
select
p.product,
t.quantity_sold,
d.yr,
d.mn
from (select distinct product from mytable) p
cross join (select distinct yr, mn from mytable) d
left join mytable t
on t.product = p.product
and t.yr = d.yr
and t.mn = d.mn
This puts nulls for rows with no sale - that's presumably a numeric column so you generally don't want to write a string like 'Not Sold' into it.
If there is a possibility of duplicate (product, yr, mn), you might want to use outer aggregation:
select
p.product,
sum(t.quantity_sold) quantity_sold,
d.yr,
d.mn
from (select distinct product from mytable) p
cross join (select distinct yr, mn from mytable) d
left join mytable t
on t.product = p.product
and t.yr = d.yr
and t.mn = d.mn
group by p.product, d.yr, d.mn

SQL - max of sum revenue by month by country

I'm trying to find the record revenue months by for each country. Below query provides me revenue by month for each country.
select d.calendar_year_month as 'Record_month',
c.country_name as 'country'
,sum(Net_qty*(unit_charge+unit_shipping_charge)) as 'Revenue'
from sensu_reporting.commercial_analysts.customer_science_transactions CST (nolock)
join Sensu.dbo.Country_D C (nolock) on cst.country_code = c.Country_Code
join sensu.dbo.Date_D d (nolock) on cst.Order_Date_Key = d.Date_Key
where cst.site_key in ('95')
and cst.order_date_key >= 20180101
group by d.calendar_year_month, c.country_name
I tried using:
select a.country,
a.record_month,
max(a.revenue) as 'Record_Revenue'
from(
select d.calendar_year_month as 'Record_month',
c.country_name as 'country'
,sum(Net_qty*(unit_charge+unit_shipping_charge)) as 'Revenue'
from sensu_reporting.commercial_analysts.customer_science_transactions CST (nolock)
join Sensu.dbo.Country_D C (nolock) on cst.country_code = c.Country_Code
join sensu.dbo.Date_D d (nolock) on cst.Order_Date_Key = d.Date_Key
where cst.site_key in ('95')
and cst.order_date_key >= 20180101
group by d.calendar_year_month, c.country_name)
a
group by country, record_month
However, this provides me the same data as the initial query. What am I doing wrong, and how do I amend my query such that it gives me only the month with the highest revenue per country?
Since I didn't know your table structure this is an simplified example with only one table (id, calendar_year_month, countrycode, revenue). I used a subquery to identify the max revenue.
select calendar_year_month, country, revenue
from reporting r
where revenue = (select max(revenue) from reporting r2 where r.country = r2.country )
group by calendar_year_month, country
And the result I got was like this
201802 NO 1500
201802 SE 3000
201803 DE 7000
201803 NO 1500
Notice there are two rows for NO.
I hope this is translatable to your table structure.
Is this what you want?
with r as (
select d.calendar_year_month as Record_month,
c.country_name as country,
sum(Net_qty*(unit_charge+unit_shipping_charge)) as Revenue
from sensu_reporting.commercial_analysts.customer_science_transactions CST join
Sensu.dbo.Country_D C
on cst.country_code = c.Country_Code join
sensu.dbo.Date_D d
on cst.Order_Date_Key = d.Date_Key
where cst.site_key in ('95') and cst.order_date_key >= 20180101
group by d.calendar_year_month, c.country_name
)
select r.*
from (select r.*,
row_number() over (partition by record_month order by revenue desc) as seqnum
from r
) r
where seqnum = 1;

Divide results in two columns depending on the input values? SQL Server

I am using the Nortwind database with SQL Server 2014, I try to make a query to divide the results of the orders in two different years, The format that I want in my query is
category |anio one | anio two
where the years may vary , What I try so far is
SELECT ca.CategoryName , YEAR(o.OrderDate), SUM(ot.UnitPrice*ot.Quantity) as total
FROM Orders o
INNER JOIN [Order Details] ot ON O.OrderID = ot.OrderID
INNER JOIN Products pro ON ot.ProductID = pro.ProductID
INNER JOIN Categories ca ON pro.CategoryID = ca.CategoryID
GROUP BY ca.CategoryName,YEAR(o.OrderDate)
ORDER BY ca.CategoryName;
This gives me the totals of each category for a different year, 1996-1997-1998 in column YEAR(o.OrderDate)
I want to get for example
CategoryName | 1996 | 1997
Beverages |53879,20 | 110424,00
Condiments |19458,30 | 59679,00
....
Use "conditional aggregates".
SELECT
ca.CategoryName
, SUM(case when year(o.OrderDate) = 1996 then ot.UnitPrice * ot.Quantity end) AS total_1996
, SUM(case when year(o.OrderDate) = 1997 then ot.UnitPrice * ot.Quantity end) AS total_1997
FROM Orders o
INNER JOIN [Order Details] ot ON o.OrderID = ot.OrderID
INNER JOIN Products pro ON ot.ProductID = pro.ProductID
INNER JOIN Categories ca ON pro.CategoryID = ca.CategoryID
where o.OrderDate >= '19960101' and o.OrderDate < '19980101'
GROUP BY
ca.CategoryName
ORDER BY
ca.CategoryName
Basically that means use a case expression inside the aggregate function.
I case you are wondering why I have not used "between in the where clause: see
Bad habits to kick : mis-handling date / range queries
You can use PIVOT to get your desired Output
BEGIN TRAN
CREATE TABLE #Temp(CategoryName NVARCHAR(50),[Year]INT,TOTAL DECIMAL(15,2))
INSERT INTO #Temp
SELECT ca.CategoryName , YEAR(o.OrderDate), SUM(ot.UnitPrice*ot.Quantity) as total
FROM Orders o
INNER JOIN [Order Details] ot ON O.OrderID = ot.OrderID
INNER JOIN Products pro ON ot.ProductID = pro.ProductID
INNER JOIN Categories ca ON pro.CategoryID = ca.CategoryID
GROUP BY ca.CategoryName,YEAR(o.OrderDate)
ORDER BY ca.CategoryName;
SELECT * FROM #Temp
GO
select *
from
(
select CategoryName, [Year], TOTAL
from #Temp
) src
pivot
(
sum(TOTAL)
for YEAR in ([1996], [1997]
)) piv;
ROLLBACK TRAN
you can use pivot to get the desired output
CREATE TABLE #TEMP
(
Category VARCHAR(200),
YEAR1 NUMERIC,
Total MONEY
)
INSERT INTO #TEMP
SELECT 'beverages', 1996, 500
union
SELECT 'beverages', 1997, 750
union
SELECT 'Condiments', 1997, 1000
union
SELECT 'Condiments', 1996, 800
SELECT *
FROM
(
SELECT Category,YEAR1, Total FROM #TEMP
) AS SourceTable
PIVOT
(
AVG(Total) FOR YEAR1 IN ( [1996], [1997])
) AS PivotTable;

SQL Pivot or Cross Tab?

select SALESTABLE.CUSTACCOUNT,
SALESTABLE.salesname,
datename(month,SALESTABLE.SHIPPINGDATECONFIRMED)as Month,
sum(SALESLINE.LineAmount ) SaleAmount
from
SALESTABLE
inner join SALESLINE
on SALESTABLE.SALESID= SALESLINE.SALESID
WHERE SUBSTRING(SALESTABLE.CUSTACCOUNT, 1,2)='O-'
group by SALESTABLE.CUSTACCOUNT,
SALESTABLE.salesname,
datename(month,SALESTABLE.SHIPPINGDATECONFIRMED)
order by SALESTABLE.salesname,
datename(month,SALESTABLE.SHIPPINGDATECONFIRMED)
can i convert this query into pivot i want to show month name at the top of the table like Column Header and sum of LineAmount bellow these month columns
select *
from (select SALESTABLE.CUSTACCOUNT,
SALESTABLE.salesname,
datename(month,SALESTABLE.SHIPPINGDATECONFIRMED)as Month,
SALESLINE.LineAmount
from
SALESTABLE
inner join SALESLINE
on SALESTABLE.SALESID= SALESLINE.SALESID
WHERE SUBSTRING(SALESTABLE.CUSTACCOUNT, 1,2)='O-'
) t pivot (sum (LineAmount) for Month in (January,February,March,April,May,June,July,August,September,October,November,December)) p