SQL join to get field name - sql

I have the following query and I'm looking to write a join to give me the direction of a stock trend based on the id.
stock_trends
------------
stock_id
trend_id
direction_id
timestamp
price
breakout_price
trend_direction
---------------
id
direction
select s.*, v.latest_trend_date,
dbo.GetStockAverageVolume(s.id, latest_trend_date, GETDATE())
as avg_volume from stocks s
join(select stock_id, MAX(timestamp)as latest_trend_date from stock_trends st
group by st.stock_id) v on v.stock_id = s.id
where
(select top 1 trend_id from stock_trends
where s.id = stock_trends.stock_id order by [timestamp] desc) =
#trend_id and s.market_id = #market_id
and dbo.GetStockAverageVolume(s.id, latest_trend_date, GETDATE()) > 300000
order by latest_trend_date desc
How can I modify the above query to get the direction of the trend based on the direction_id within the stock_trends table?
For example:
select s.*, v.latest_trend_date,
dbo.GetStockAverageVolume(s.id, latest_trend_date, GETDATE())
as avg_volume, **direction** from stock s
...
...
...
Man I'm bad at joins!
Thanks so much.

This should work, but I think your query could be optimized
select stock_id, td.direction, MAX(timestamp) as latest_trend_date
from stock_trends st
join trend_direction td on st.direction_id = td.id
group by st.stock_id, td.direction
oh yeah and add v.direction to the main list.

Related

SQL get top 3 values / bottom 3 values with group by and sum

I am working on a restaurant management system. There I have two tables
order_details(orderId,dishId,createdAt)
dishes(id,name,imageUrl)
My customer wants to see a report top 3 selling items / least selling 3 items by the month
For the moment I did something like this
SELECT
*
FROM
(SELECT
SUM(qty) AS qty,
order_details.dishId,
MONTHNAME(order_details.createdAt) AS mon,
dishes.name,
dishes.imageUrl
FROM
rms.order_details
INNER JOIN dishes ON order_details.dishId = dishes.id
GROUP BY order_details.dishId , MONTHNAME(order_details.createdAt)) t
ORDER BY t.qty
This gives me all the dishes sold count order by qty.
I have to manually filter max 3 records and reject the rest. There should be a SQL way of doing this. How do I do this in SQL?
You would use row_number() for this purpose. You don't specify the database you are using, so I am guessing at the appropriate date functions. I also assume that you mean a month within a year, so you need to take the year into account as well:
SELECT ym.*
FROM (SELECT YEAR(od.CreatedAt) as yyyy,
MONTH(od.createdAt) as mm,
SUM(qty) AS qty,
od.dishId, d.name, d.imageUrl,
ROW_NUMBER() OVER (PARTITION BY YEAR(od.CreatedAt), MONTH(od.createdAt) ORDER BY SUM(qty) DESC) as seqnum_desc,
ROW_NUMBER() OVER (PARTITION BY YEAR(od.CreatedAt), MONTH(od.createdAt) ORDER BY SUM(qty) DESC) as seqnum_asc
FROM rms.order_details od INNER JOIN
dishes d
ON od.dishId = d.id
GROUP BY YEAR(od.CreatedAt), MONTH(od.CreatedAt), od.dishId
) ym
WHERE seqnum_asc <= 3 OR
seqnum_desc <= 3;
Using the above info i used i combination of group by, order by and limit
as shown below. I hope this is what you are looking for
SELECT
t.qty,
t.dishId,
t.month,
d.name,
d.mageUrl
from
(
SELECT
od.dishId,
count(od.dishId) AS 'qty',
date_format(od.createdAt,'%Y-%m') as 'month'
FROM
rms.order_details od
group by date_format(od.createdAt,'%Y-%m'),od.dishId
order by qty desc
limit 3) t
join rms.dishes d on (t.dishId = d.id)

SQLite print out history of daily price changes of a stock

I have to output price change for each stock based on the dates that are held at the table pricedates.
What I have currently come up with is to group prices by id_stock and order by price_date in ASC order, then subtract from every row.
It should be separately done for buy_price and sell_price.
The logic should be like this
stock_id prices history
1 price 1 price 1
price 60 price 60-price1
price 98 price98-price60
2 price 34 price34
price 67 price67-price34
I am really struggling with putting everything together, here is something that I have tried, most probably it's a total mess:
select p.id_price
(select min(pd.price_date) from pricedates pd as po where po.price_date > pd.price_date) as next_id_price,
(po.buy_price - pd.buy_price) buy
(po.sell_price - pd.sell_price) sell
from prices p
left join pricedates pd on pd.id_date=p.id_price_date
group by p.id_stock
order by pd.price_date asc;
If you are running SQLite >= 3.25, you can use window function lag() to access the previous record of the same stock_id:
select
p.stock_id,
p.buy_price,
p.buy_price - lag(p.buy_price, 1, 0)
over(partition by p.stock_id order by pd.price_date) buy_history,
p.sell_price,
p.sell_price - lag(p.sell_price, 1, 0)
over(partition by p.stock_id order by pd.price_date) sell_history
from prices p
inner join pricedates pd
on pd.id_date = p.id_price_date
order by pd.price_date asc;
In earlier vesions of SQLite, where lag() is not available, one solution is to self-join the table with a correlated subquery that locates the previous record:
select
p.stock_id,
p.buy_price,
p.buy_price - coalesce(p1.buy_price, 0) buy_history,
p.sell_price,
p.sell_price - coalesce(p1.sell_price, 0) sell_history
from prices p
inner join pricedates pd
on pd.id_date = p.id_price_date
left join prices p1
on p1.stock_id = p.stock_id
and p1.id_date = (
select pd1.id_date
from pricedates pd1
where pd1.price_date > pd.price_date
order by pd1.price_date
limit 1
)
order by pd.price_date asc;
You would use lag() -- which is available in the more recent versions of SQLite:
select p.*,
(price -
lag(p.price, 1, 0) over (partition by stock_id order by id_price_date)
) as diff
from price p;

Trying to create a SQL query

I am trying to create a query that retrieves only the ten companies with the highest number of pickups over the six-month period, this means pickup occasions, and not the number of items picked up.
I have done this
SELECT *
FROM customer
JOIN (SELECT manifest.pickup_customer_ref reference,
DENSE_RANK() OVER (PARTITION BY manifest.pickup_customer_ref ORDER BY COUNT(manifest.trip_id) DESC) rnk
FROM manifest
INNER JOIN trip ON manifest.trip_id = trip.trip_id
WHERE trip.departure_date > TRUNC(SYSDATE) - interval '6' month
GROUP BY manifest.pickup_customer_ref) cm ON customer.reference = cm.reference
WHERE cm.rnk < 11;
this uses dense_rank to determine the order or customers with the highest number of trips first
Hmm well i don't have Oracle so I can't test it 100%, but I believe your looking for something like the following:
Keep in mind that when you use group by, you have to narrow down to the same fields you group by in the select. Hope this helps at least give you an idea of what to look at.
select TOP 10
c.company_name,
m.pickup_customer_ref,
count(*) as 'count'
from customer c
inner join mainfest m on m.pickup_customer_ref = c.reference
inner join trip t on t.trip_id = m.trip_id
where t.departure_date < DATEADD(month, -6, GETDATE())
group by c.company_name, m.pickup_customer_ref
order by 'count', c.company_name, m.pickup_customer_ref desc

Incorrect sum when I join a second table

This is the first time I ask for your help,
Actually I have to create a query, and did a similar example for it. I have two tables,
Report (ReportID, Date, headCount)
Production(ProdID, ReportID, Quantity)
My question is using this query, I get a wrong result,
SELECT
Report.date,
SUM(Report.HeadCount) AS SumHeadCount,
SUM(Production.Quantity) AS SumQuantity
FROM
Report
INNER JOIN
Production ON Report.ReportID = Production.ReportID
GROUP BY
Date
ORDER BY
Date
I guess some rows are being counted more than once, could you please give me a hand?
EDIT
if i run a query to get a sum of headcount grouped by day, I get:
date Headcount
7/2/2012 1843
7/3/2012 1802
7/4/2012 1858
7/5/2012 1904
also for Production Qty I get:
2012-07-02 8362
2012-07-03 8042
2012-07-04 8272
2012-07-05 9227
but when i combine the both queries i get i false one, i expect on 2 july 8362 qty against 1843, but i get:
day TotalHeadcount totalQty
7/2/2012 6021 8362
7/3/2012 7193 8042
7/4/2012 6988 8272
7/5/2012 7197 9227
This may be helpful
SELECT Report.ReportDate,
Sum(Report.HeadCount) AS SumHeadCount,
ProductionSummary.SumQuantity
FROM Report
INNER JOIN (SELECT ReportID,
Sum(Production.Quantity) AS SumQuantity
FROM Production
GROUP BY ReportID) AS ProductionSummary
ON Report.ReportID = ProductionSummary.ReportID
GROUP BY ReportDate
ORDER BY ReportDate
One way of avoiding this (subject to RDBMS support) would be
WITH R
AS (SELECT *,
Sum(HeadCount) OVER (PARTITION BY date) AS SumHeadCount
FROM Report)
SELECT R.date,
SumHeadCount,
Sum(P.Quantity) AS SumQuantity
FROM R
JOIN Production P
ON R.ReportID = P.ReportID
GROUP BY R.date, SumHeadCount
ORDER BY R.date
Group records per date using following
SELECT ReportSummary.ReportDate, SUM(ReportSummary.SumHeadCount) AS SumHeadCount, SUM(ProductionSummary.SumQuantity) AS SumQuantity
FROM
(
SELECT Report.ReportDate, SUM(Report.HeadCount) AS SumHeadCount
FROM Report
GROUP BY Report.ReportDate
) AS ReportSummary
INNER JOIN
(
SELECT Report.ReportDate, Sum(Production.Quantity) AS SumQuantity
FROM Production
INNER JOIN Report
ON Report.ReportID = Production.ReportID
GROUP BY Report.ReportDate
) AS ProductionSummary
ON ReportSummary.ReportDate = ProductionSummary.ReportDate
GROUP BY ReportSummary.ReportDate
ORDER BY ReportSummary.ReportDate
We have same problem but I solved it. Try this.
SELECT tbl_report.SumHeadCount, tbl_report.date, tbl_production.SumQuantity
FROM
( select date,
SUM(HeadCount) AS SumHeadCount FROM Report GROUP by date)as tbl_report
JOIN
( select SUM(Quantity) AS SumQuantity, date FROM Production GROUP by date)as tbl_production
WHERE tbl_report.date = tbl_production.date

MySQL: Returning multiple columns from an in-line subquery

I'm creating an SQL statement that will return a month by month summary on sales.
The summary will list some simple columns for the date, total number of sales and the total value of sales.
However, in addition to these columns, i'd like to include 3 more that will list the months best customer by amount spent. For these columns, I need some kind of inline subquery that can return their ID, Name and the Amount they spent.
My current effort uses an inline SELECT statement, however, from my knowledge on how to implement these, you can only return one column and row per in-line statement.
To get around this with my scenario, I can of course create 3 separate in-line statements, however, besides this seeming impractical, it increases the query time more that necessary.
SELECT
DATE_FORMAT(OrderDate,'%M %Y') AS OrderMonth,
COUNT(OrderID) AS TotalOrders,
SUM(OrderTotal) AS TotalAmount,
(SELECT SUM(OrderTotal) FROM Orders WHERE DATE_FORMAT(OrderDate,'%M %Y') = OrderMonth GROUP BY OrderCustomerFK ORDER BY SUM(OrderTotal) DESC LIMIT 1) AS TotalCustomerAmount,
(SELECT OrderCustomerFK FROM Orders WHERE DATE_FORMAT(OrderDate,'%M %Y') = OrderMonth GROUP BY OrderCustomerFK ORDER BY SUM(OrderTotal) DESC LIMIT 1) AS CustomerID,
(SELECT CustomerName FROM Orders INNER JOIN Customers ON OrderCustomerFK = CustomerID WHERE DATE_FORMAT(OrderDate,'%M %Y') = OrderMonth GROUP BY OrderCustomerFK ORDER BY SUM(OrderTotal) DESC LIMIT 1) AS CustomerName
FROM Orders
GROUP BY DATE_FORMAT(OrderDate,'%m%y')
ORDER BY DATE_FORMAT(OrderDate,'%y%m') DESC
How can i better structure this query?
FULL ANSWER
After some tweaking of Dave Barkers solution, I have a final version for anyone in the future looking for help.
The solution by Dave Barker worked perfectly with the customer details, however, it made the simpler Total Sales and Total Sale Amount columns get some crazy figures.
SELECT
Y.OrderMonth, Y.TotalOrders, Y.TotalAmount,
Z.OrdCustFK, Z.CustCompany, Z.CustOrdTotal, Z.CustSalesTotal
FROM
(SELECT
OrdDate,
DATE_FORMAT(OrdDate,'%M %Y') AS OrderMonth,
COUNT(OrderID) AS TotalOrders,
SUM(OrdGrandTotal) AS TotalAmount
FROM Orders
WHERE OrdConfirmed = 1
GROUP BY DATE_FORMAT(OrdDate,'%m%y')
ORDER BY DATE_FORMAT(OrdDate,'%Y%m') DESC)
Y INNER JOIN
(SELECT
DATE_FORMAT(OrdDate,'%M %Y') AS CustMonth,
OrdCustFK,
CustCompany,
COUNT(OrderID) AS CustOrdTotal,
SUM(OrdGrandTotal) AS CustSalesTotal
FROM Orders INNER JOIN CustomerDetails ON OrdCustFK = CustomerID
WHERE OrdConfirmed = 1
GROUP BY DATE_FORMAT(OrdDate,'%m%y'), OrdCustFK
ORDER BY SUM(OrdGrandTotal) DESC)
Z ON Z.CustMonth = Y.OrderMonth
GROUP BY DATE_FORMAT(OrdDate,'%Y%m')
ORDER BY DATE_FORMAT(OrdDate,'%Y%m') DESC
Move the inline SQL to be a inner join query. So you'd have something like...
SELECT DATE_FORMAT(OrderDate,'%M %Y') AS OrderMonth, COUNT(OrderID) AS TotalOrders, SUM(OrderTotal) AS TotalAmount, Z.OrderCustomerFK, Z.CustomerName, z.OrderTotal as CustomerTotal
FROM Orders
INNER JOIN (SELECT DATE_FORMAT(OrderDate,'%M %Y') as Mon, OrderCustomerFK, CustomerName, SUM(OrderTotal) as OrderTotal
FROM Orders
GROUP BY DATE_FORMAT(OrderDate,'%M %Y'), OrderCustomerFK, CustomerName ORDER BY SUM(OrderTotal) DESC LIMIT 1) Z
ON Z.Mon = DATE_FORMAT(OrderDate,'%M %Y')
GROUP BY DATE_FORMAT(OrderDate,'%m%y'), Z.OrderCustomerFK, Z.CustomerName
ORDER BY DATE_FORMAT(OrderDate,'%y%m') DESC
You can also do something like:
SELECT
a.`y`,
( SELECT #c:=NULL ) AS `temp`,
( SELECT #d:=NULL ) AS `temp`,
( SELECT
CONCAT(#c:=b.`c`, #d:=b.`d`)
FROM `b`
ORDER BY b.`uid`
LIMIT 1 ) AS `temp`,
#c as c,
#d as d
FROM `a`
Give this a shot:
SELECT CONCAT(o.order_month, ' ', o.order_year),
o.total_orders,
o.total_amount,
x.sum_order_total,
x.ordercustomerfk,
x.customername
FROM (SELECT MONTH(t.orderdate) AS order_month,
YEAR(t.orderdate) AS order_year
COUNT(t.orderid) AS total_orders,
SUM(t.ordertotal) AS total_amount
FROM ORDERS t
GROUP BY MONTH(t.orderdate), YEAR(t.orderdate)) o
JOIN (SELECT MONTH(t.orderdate) AS ordermonth,
YEAR(t.orderdate) AS orderyear
SUM(t.ordertotal) 'sum_order_total',
t.ordercustomerfk,
c.customername
FROM ORDERS t
JOIN CUSTOMERS c ON c.customerid = o.ordercustomerfk
GROUP BY t.ordercustomerfk, MONTH(t.orderdate), YEAR(t.orderdate)) x ON x.order_month = o.order_month
AND x.order_year = o.order_year
ORDER BY o.order_year DESC, o.order_month DESC