Need to optimize query to use in an indexed view - sql

I have this query in a view that I cannot index because of the referenced table t. I've converted to a sub-query which doesn't work either and is a smidgen slower anyway.
I simply need the last price a productid was sold for.
SELECT op.productId, op.Price, o.createdat
from (
SELECT max(op.OrderProductid), MAX(o.OrderId)
FROM dbo.OrderProduct op
INNER JOIN dbo.Order so ON op.OrderId = so.OrderId
WHERE o.StatusId IN (1,2,3)
GROUP BY op.ProductId
) t
join OrderProduct op on op.Productid = t.Productid
join Order o ON o.OrderId = op.OrderId

Considering your last statement:
I simply need the last price a productid was sold for.
You can use row_number for this:
select *
from (
select op.productid, op.price, o.createdat,
row_number() over (partition by op.productid order by o.orderid desc) rn
from orderproduct op
join order o on op.orderid = o.orderid
) t
where rn = 1

Related

How to select top 300 for each orders total order item value

I want to view the top 300 items ordered by total net price, how do I do this please?
Using SSMS 2014
If I remove group by I get error: Column 'orderitems.orderid' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Please see workings below:
select top 300
orderitems.orderid, orders.traderid, orders.orderdate,
SUM(orderitems.nettprice) AS nettprice
from orderitems
INNER JOIN orders ON orders.tradertype = 'S' AND orders.id =
orderitems.orderid
where orderitems.ordertype = 'PO'
group by orderitems.orderid, orders.traderid, orders.orderdate,
orderitems.nettprice
order by orderitems.nettprice DESC
You need to order by the SUM value. You can either put that in the ORDER BY explicitly, or you can use the SELECT column name without a table reference (in other words the column alias you use in the SELECT)
I strongly recommend you use short table aliases to make your code more readable
select top 300
oi.orderid,
o.traderid,
o.orderdate,
SUM(oi.nettprice) AS nettprice
from orderitems AS oi
INNER JOIN orders AS o ON o.tradertype = 'S' AND o.id = oi.orderid
where oi.ordertype = 'PO'
group by
oi.orderid, o.traderid, o.orderdate
order by
nettprice DESC
-- alternatively
order by
SUM(oi.nettprice) DESC
Perhaps what you need here is a windowed SUM and then a TOP. The PARTITION BY clause is based on this comment:
SELECT TOP (300)
oi.orderid,
o.traderid,
o.orderdate,
SUM(oi.nettprice) OVER (PARTITION BY oi.itemnumber, oi.partid, oi.quantity) AS totalnettprice
FROM dbo.orderitems oi
INNER JOIN dbo.orders o ON o.id = oi.orderid
WHERE o.tradertype = 'S'
AND oi.ordertype = 'PO'
ORDER BY oi.totalnettprice DESC;
Very simple:
select top 300
itm.orderid,
ord.traderid,
ord.orderdate,
SUM(itm.nettprice) AS price
from orderitems itm
INNER JOIN orders ord ON
ord.tradertype = 'S' AND ord.id = itm.orderid
where itm.ordertype = 'PO'
group by
itm.orderid,
ord.traderid,
ord.orderdate
order by price DESC

Sql Query for Identified by duplicates and remove in it

SELECT i.product_id, o.date_added
FROM ims_order_product i
INNER JOIN ims_order o
WHERE i.product_id IN (SELECT p.product_Id FROM ims_product p)
The Query Return Product_id column and Date column But Product_id column contain duplicates So I want remove duplicates in Product_id based on Date_added by recent Date Can any give the answer.
Try grouping by product_id. Below query will select the latest date for duplicate product_id
SELECT i.product_id, MAX(o.date_added) FROM ims_order_product i INNER JOIN ims_order o WHERE i.product_id IN (SELECT p.product_Id FROM ims_product p) GROUP BY i.product_id
Please try this query
SELECT i.product_id, MAX(o.date_added) date_added
FROM ims_order_product i
INNER JOIN ims_order o --add your join condition here
WHERE EXISTS (SELECT p.product_Id FROM ims_product p WHERE p.product_Id = i.product_id)
GROUP BY i.product_id
SELECT * FROM (SELECT i.product_id, o.date_added, o.order_id FROM ims_order_product i
JOIN ims_order o ON o.order_id = i.order_id
JOIN ims_product pr ON pr.product_id = i.product_id ORDER BY date_added DESC) AS sub GROUP BY product_id

Last order item in Oracle SQL

I need to list columns from customer table, the date from first order and all data from last one, in a 1:N relationship between customer and order tables. I'm using Oracle 10g.
How the best way to do that?
TABLE CUSTOMER
---------------
id NUMBER
name VARCHAR2(200)
subscribe_date DATE
TABLE ORDER
---------------
id NUMBER
id_order NUMBER
purchase_date DATE
purchase_value NUMBER
Here is one way of doing it, using the row_number function, one join, and on aggregation:
select c.*,
min(o.purchase_date) as FirstPurchaseDate,
min(case when seqnum = 1 then o.id_order end) as Last_IdOrder,
min(case when seqnum = 1 then o.purchase_date end) as Last_PurchaseDate,
min(case when seqnum = 1 then o.purchase_value end) as Last_PurchaseValue
from Customer c join
(select o.*,
row_number() over (partition by o.id order by purchase_date desc) as seqnum
from orders o
) o
on c.customer_id = o.order_id
group by c.customer_id, c.name, c.subscribe_date
It's not obvious how to join the customer table to the orders table (order is a reserved word in Oracle so your table can't be named order). If we assume that the id_order in orders joins to the id in customer
SELECT c.id customer_id,
c.name name,
c.subscribe_date,
o.first_purchase_date,
o.id last_order_id,
o.purchase_date last_order_purchase_date,
o.purchase_value last_order_purchase_value
FROM customer c
JOIN (SELECT o.*,
min(o.purchase_date) over (partition by id_order) first_purchase_date,
rank() over (partition by id_order order by purchase_date desc) rnk
FROM orders o) o ON (c.id = o.id_order)
WHERE rnk = 1
I'm confused by your field names, but I'm going to assume that ORDER.id is the id in the CUSTOMER table.
The earliest order date is easy.
select CUSTOMER.*, min(ORDER.purchase_date)
from CUSTOMER
inner join ORDER on CUSTOMER.id = ORDER.id
group by CUSTOMER.*
To get the last order data, join this to the ORDER table again.
select CUSTOMER.*, min(ORD_FIRST.purchase_date), ORD_LAST.*
from CUSTOMER
inner join ORDER ORD_FIRST on CUSTOMER.id = ORD_FIRST.id
inner join ORDER ORD_LAST on CUSTOMER.id = ORD_LAST.id
group by CUSTOMER.*, ORD_LAST.*
having ORD_LAST.purchase_date = max(ORD_FIRST.purchase_date)
Maybe something like this assuming the ID field in the Order table is actually the Customer ID:
SELECT C.*, O1.*, O2.purchase_Date as FirstPurchaseDate
FROM Customer C
LEFT JOIN
(
SELECT Max(purchase_date) as pdate, id
FROM Orders
GROUP BY id
) MaxPurchaseOrder
ON C.Id = MaxPurchaseOrder.Id
LEFT JOIN Orders O1
ON MaxPurchaseOrder.pdate = O1.purchase_date
AND MaxPurchaseOrder.id = O1.id
LEFT JOIN
(
SELECT Min(purchase_date) as pdate, id
FROM Orders
GROUP BY id
) MinPurchaseOrder
ON C.Id = MinPurchaseOrder.Id
LEFT JOIN Orders O2
ON MinPurchaseOrder.pdate = O2.purchase_date
AND MinPurchaseOrder.id = O2.id
And the sql fiddle.

SQL Query for counting number of orders per customer and Total Dollar amount

I have two tables
Order with columns:
OrderID,OrderDate,CID,EmployeeID
And OrderItem with columns:
OrderID,ItemID,Quantity,SalePrice
I need to return the CustomerID(CID), number of orders per customer, and each customers total amount for all orders.
So far I have two separate queries. One gives me the count of customer orders....
SELECT CID, Count(Order.OrderID) AS TotalOrders
FROM [Order]
Where CID = CID
GROUP BY CID
Order BY Count(Order.OrderID) DESC;
And the other gives me the total sales. I'm having trouble combining them...
SELECT CID, Sum(OrderItem.Quantity*OrderItem.SalePrice) AS TotalDollarAmount
FROM OrderItem, [Order]
WHERE OrderItem.OrderID = [Order].OrderID
GROUP BY CID
I'm doing this in Access 2010.
You would use COUNT(DISTINCT ...) in other SQL engines:
SELECT CID,
Count(DISTINCT O.OrderID) AS TotalOrders,
Sum(OI.Quantity*OI.SalePrice) AS TotalDollarAmount
FROM [Order] O
INNER JOIN [OrderItem] OI
ON O.OrderID = OI.OrderID
GROUP BY CID
Order BY Count(DISTINCT O.OrderID) DESC
Which Access unfortunately does not support. Instead you can first get the Order dollar amounts and then join them before figuring the order counts:
SELECT CID,
COUNT(Orders.OrderID) AS TotalOrders,
SUM(OrderAmounts.DollarAmount) AS TotalDollarAmount
FROM [Orders]
INNER JOIN (SELECT OrderID, Sum(Quantity*SalePrice) AS DollarAmount
FROM OrderItems GROUP BY OrderID) AS OrderAmounts
ON Orders.OrderID = OrderAmounts.OrderID
GROUP BY CID
ORDER BY Count(Orders.OrderID) DESC
If you need to include Customers that have orders with no items (unusual but possible), change INNER JOIN to LEFT OUTER JOIN.
Create a query which uses your 2 existing queries as subqueriers, and join the 2 subqueries on CID. Define your ORDER BY in the parent query instead of in a subquery.
SELECT
sub1.CID,
sub1.TotalOrders,
sub2.TotalDollarAmount
FROM
(
SELECT
CID,
Count(Order.OrderID) AS TotalOrders
FROM [Order]
GROUP BY CID
) AS sub1
INNER JOIN
(
SELECT
CID,
Sum(OrderItem.Quantity*OrderItem.SalePrice)
AS TotalDollarAmount
FROM OrderItem INNER JOIN [Order]
ON OrderItem.OrderID = [Order].OrderID
GROUP BY CID
) AS sub2
ON sub1.CID = sub2.CID
ORDER BY sub1.TotalOrders DESC;

SQL: improving join efficiency

If I turn this sub-query which selects sales persons and their highest price paid for any item they sell:
select *,
(select top 1 highestProductPrice
from orders o
where o.salespersonid = s.id
order by highestProductPrice desc ) as highestProductPrice
from salespersons s
in to this join in order to improve efficiency:
select *, highestProductPrice
from salespersons s join (
select salespersonid, highestProductPrice, row_number(
partition by salespersonid
order by salespersonid, highestProductPrice) as rank
from orders ) o on s.id = o.salespersonid
It still touches every order record (it enumerates the entire table before filtering by salespersonid it seems.) However you cannot do this:
select *, highestProductPrice
from salespersons s join (
select salespersonid, highestProductPrice, row_number(
partition by salespersonid
order by salespersonid, highestProductPrice) as rank
from orders
where orders.salepersonid = s.id) o on s.id = o.salespersonid
The where clause in the join causes a `multi-part identifier "s.id" could not be bound.
Is there any way to join the top 1 out of each order group with a join but without touching each record in orders?
Try
SELECT
S.*,
T.HighestProductPrice
FROM
SalesPersons S
CROSS APPLY
(
SELECT TOP 1 O.HighestProductPrice
FROM Orders O
WHERE O.SalesPersonid = S.Id
ORDER BY O.SalesPersonid, O.HighestProductPrice DESC
) T
would
select s.*, max(highestProductPrice)
from salespersons s
join orders o on o.salespersonid = s.id
group by s.*
or
select s.*, highestProductPrice
from salespersons s join (select salepersonid,
max(highestProductPrice) as highestProductPrice
from orders o) as o on o.salespersonid = s.id
work?