Filter by virtual column? - sql

I have the following database structure :
[Order]
OrderId
Total
[Payment]
OrderId
Amount
Every Order can have X payment rows. I want to get only the list of orders where the sum of all the payments are < than the order Total.
I have the following SQL but I will return all the orders paid and unpaid.
SELECT o.OrderId,
o.UserId,
o.Total,
o.DateCreated,
COALESCE(SUM(p.Amount),0) AS Paid
FROM [Order] o
LEFT JOIN Payment p ON p.OrderId = o.OrderId
GROUP BY o.OrderId, o.Total, o.UserId, o.DateCreated
I have tried to add Where (Paid < o.Total) but it does not work, any idea?
BTM I'm using SQL CE 3.5

What you are looking for is the HAVING clause.
Instead of "Where (COALESCE(SUM(p.Amount),0) < o.Total)", try "HAVING (COALESCE(SUM(p.Amount),0) < o.Total)"
Check out the MSDN Reference on HAVING.

Select Order.* From Order
Left Join
(Select OrderID, Sum(Amount) As t From Payment Group By OrderID) As s On s.OrderID = Oder.OrderID
Where IsNULL(s.t,0) < Order.Total

Related

New record after a year of no records

I want to find returning customers who have placed an order after a year without orders. I have managed the below but am having a tough time adding the year gap. Something like "and count of orders between dates = 0"... any ideas would be appreciated, I cant seem to figure out the required syntax at all.
SELECT
Min(Orders.[Order Date]) AS [MinOfOrder Date],
Max(Orders.[Order Date]) AS [MaxOfOrder Date],
Orders.CustomerID
FROM
Customers
INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID
GROUP BY
Orders.CustomerID
HAVING
(
((Min(Orders.[Order Date])) < Date() -365)
AND ((Max(Orders.[Order Date])) > Date() -30)
);
You can use exists and not exists to get the first order after a year gap:
select o.*
from orders as o
where exists (select 1
from orders as o2
where o2.customerid = o.customerid and
o2.orderdate < dateadd("yyyy", o.orderdate, -1)
) and
not exists (select 1
from orders as o2
where o2.customerid = o.customerid and
o2.orderdate >= dateadd("yyyy", o.orderdate, -1) and
o2.orderdate < o.orderdate
);
You can join in the customers information if you need that.
Selecting records based on values in other records is tricky. Use a correlated subquery to pull value from another record. Need a unique identifier field - autonumber should serve. Consider:
Query1:
SELECT Orders.*, (SELECT Max(OrderDate)
FROM Orders AS Dupe
WHERE Dupe.CustomerID = Orders.CustomerID
AND Dupe.ID < Orders.ID) AS PrevOrderDate
FROM Orders;
Query2:
SELECT Query1.* FROM Query1 WHERE ((([OrderDate]-[PrevOrderDate])>365));

SQL: payments and orders query

I have 2 tables...
the first one "orders" collects
memberid
order_id
authorization date
the 2nd one "payments" collects
memberid
paymentid
payment number
last_udpate
a memberid might contain more than 1 card and I need to create a query that brings me the orderid and which payment number has used on each order taking into account when was the last update and authorization date..
I've got till here..
SELECT orders.order_id, payments.payments_number
FROM orders
JOIN payments
ON orders.memberid = payments.memberid
I'm a bit lost what subquery should I use to make it work..
Thanks!
Hmmm . . . I think a correlated subquery does what you want:
select o.*,
(select p.payment_number
from payments p
where p.memberid = o.memberid and
p.last_update <= o.authorization_date
order by last_update desc
limit 1
) as payment_number
from orders o;
You can use an additional join if you need other columns from payments.
Since you specify the cardinality of 1 to many and I am not sure of which way your cardinality is most accurately represented.
If each order has many payments then maybe you want something like this.
SELECT o.memberid,
o.order_id,
o.[authorization date],
p.payments_number,
p.last_udpate
FROM orders o
LEFT JOIN payments p
ON o.memberid = p.memberid
If it is the other way round, maybe something like this
SELECT o.memberid,
o.order_id,
o.[authorization date],
p.payments_number,
p.last_udpate
FROM payments p
LEFT JOIN orders o
ON o.memberid = p.memberid
Something similar to what was proposed but using a CTE could be:
;
with cte_partial_results as
(
SELECT o.memberid,
o.order_id,
o.[authorization date] as [authorization_date],
p.paymentid,
p.[payment number] as payment_number
p.last_update,
ROW_NUMBER() OVER(PARTITION BY o.memberid, o.order_id ORDER BY p.last_udpate desc) as RowNo
FROM orders o
LEFT JOIN payments p ON o.memberid = p.memberid
WHERE
p.last_udpate <= o.[authorization date]
)
SELECT
memberid,
order_id,
payment_number,
last_update as most_recent_update
--add other cols you want.
FROM
cte_partial_results
WHERE
RowNo = 1

Access Query from multiple tables

I currently have an Access Database with a few tables, among them Order, OrderDetails and Client.
Order (OrderID, TimeStamp, FKEmployeeID, FKClientID, OrderStatus, Comments)
OrderDetails (OrderDetailsID, FKOrderID, FKProductID, Quantity, Cost, Total Cost)
Client (ClientID, Name)
I'm trying to build a query where I can get the total orders that a client has made and the total Items.
Example:
Customer, Total Orders, Total Items
John, 5, 15
Alex, 2, 30
Ana, 1, 3
Whenever I try to make a query Total Orders and Total Items give me the same number.
Any help would be greatly appreciated!
This is supported even by ms access:
SELECT c.Name,
(select count(*)
from Orders o
where o. FKClientID = C.ClientID) as [Total Orders],
(select sum(Quantity) as Items
from OrderDetails od
inner join Order o on o.OrderID = od.FKOrderID
where o.FKClientId = C.ClientID) as [Total Items]
from Client c;
Unfortunately, MS Access doesn't support COUNT(DISTINCT). You can do this with two aggregations:
SELECT c.[Name], COUNT(*) As NumOrders, SUM(o.NumItems) As NumItems
FROM Client as c INNER JOIN
(SELECT o.OrderID, o.FKClientID, COUNT(*) As NumItems
FROM [Order] as o INNER JOIN
OrderDetails as od
ON od.FKOrderID = o.OrderID
GROUP BY o.OrderID, o.FKClientID
) as o
ON o.FKClientID = c.ClientId
GROUP BY c.ClientId, c.Name;
What about this solution (clients without orders are left out hereby):
SELECT Client.[Name],
Count(myTotalItems.OrderID) As TotalOrders,
Sum(myTotalItems.TotalItems) As TotalItems
FROM Client,
(SELECT First([Order].OrderID) As OrderID,
First([Order].FKClientID) As
ClientID,
Count(OrderDetails.OrderDetailsID) As TotalItems
FROM [Order], OrderDetails
WHERE OrderDetails.FKOrderID like [Order].OrderID
GROUP BY [Order].OrderID) As myTotalItems
WHERE myTotalItems.ClientID like Client.ClientID
GROUP BY Client.[Name];
It would be easier for you if you divide the task into multiple queries. I am giving the sample using Northwind database, you can test and see on it. Note that the structures and fieldnames are very similar to yours.
First create one that gets ClientId, OrderId, OrderDate and Sum of Quantity.
SELECT c.CustomerId, o.OrderId, o.OrderDate, sum(od.Quantity) AS Qty
FROM (Customers AS c INNER JOIN Orders AS o ON c.CustomerId = o.CustomerId)
INNER JOIN [Order Details] AS od ON o.OrderId = od.OrderID
GROUP BY c.CustomerId, o.OrderId, o.OrderDate;
Save this as "OrderOfClients" (it would be saved in Queries). Next create a query that uses this one and asks for date range:
SELECT c.CustomerId, c.CompanyName,
Count(*) AS [Total Orders],
Sum(Qty) AS [Total Items]
FROM Customers AS c
INNER JOIN OrdersOfClients AS co ON c.CustomerId = co.CustomerId
WHERE co.OrderDate Between [#startDate] And [#endDate]
GROUP BY c.CustomerId, c.CompanyName;
You can save this one as "OrdersOfClientsSummary" and call for your report.
PS: In my personal opinion, if you use a database other than access you would be doing yourself a big favor.

sql use aggregate function that counts a unique value with group by using inner joins

I searched and found similar questions online but not my particular one, they all use where or having clause.If theres one similar to mine please link it. It's a 2 part question and I have the first one done. Thank you in advance.
Okay so heres the question, part 1
"Find by customer, the total cost and the total discounted cost for each product on the order ?".
It also asks to use inner joins to find the customer and order it a specific way. Below is the answer.
SELECT
C.companyname, O.orderid, O.orderdate, P.productname,
OD.orderid, OD.unitprice, OD.qty, OD.discount,
(OD.unitprice * OD.qty - (OD.qty * OD.discount)) AS TotalCost,
(OD.qty * OD.discount) AS TotalDiscountedCost
FROM
Sales.Customers AS C
INNER JOIN
Sales.Orders AS O ON C.custid = O.custid
INNER JOIN
Sales.OrderDetails OD ON O.orderid = OD.orderid
INNER JOIN
Production.Products as P ON OD.productid = P.productid
ORDER BY
C.companyname, O.orderdate;
Now the second question is to
follow up and resume the first one by "customer and the order date year, the total cost and the total discounted cost on the order ?". It also asks for this, "Project following columns in the select clause as.
GroupByColumns.companyname
GroupByColumns.OrderdateYear
AggregationColumns.CountNumberOfIndividualOrders
AggregationColumns.CountNumberOfProductsOrders
AggregationColumns.TotalCost
AggregationColumns.TotalDiscountedCost
Finally to order by company name and orderdateYear( which are groups). Where im stuck is how to count the specific orders of qty that equal 1 as an aggregate function in the SELECT clause. I know it has to use the aggregate function COUNT because of the GROUP BY, just don't know how to. This is what I have.
SELECT
C.companyname, YEAR(O.orderdate) AS orderyear,OD.qty,
-- Where in the count function or if theres another way do I count all the
--single orders
--COUNT(OD.qty) AS indiviualorders,
(OD.unitprice * OD.qty - (OD.qty * OD.discount)) AS TotalCost,
(OD.qty * OD.discount) AS TotalDiscountedCost
FROM
Sales.Customers AS C
INNER JOIN
Sales.Orders AS O ON C.custid = O.custid
INNER JOIN
Sales.OrderDetails OD ON O.orderid = OD.orderid
INNER JOIN
Production.Products as P ON OD.productid = P.productid
GROUP BY
C.companyname, YEAR(O.orderdate)
ORDER BY
C.companyname, O.orderdate;
You case use a case statement inside a sum
SUM(CASE WHEN <xyz> THEN 1 ELSE 0 END)
But for the count of unique orders, use SELECT(DISTINCT ) on a key that is unique in the order table
SELECT COUNT(DISTINCT O.OrderID) As DistinctOrders FROM Table

Getting max value before given date

I am pretty new to using MS SQL 2012 and I am trying to create a query that will:
Report the order id, the order date and the employee id that processed the order
report the maximum shipping cost among the orders processed by the same employee prior to that order
This is the code that I've come up with, but it returns the freight of the particular order date. Whereas I am trying to get the maximum freight from all the orders before the particular order.
select o.employeeid, o.orderid, o.orderdate, t2.maxfreight
from orders o
inner join
(
select employeeid, orderdate, max(freight) as maxfreight
from orders
group by EmployeeID, OrderDate
) t2
on o.EmployeeID = t2.EmployeeID
inner join
(
select employeeid, max(orderdate) as mostRecentOrderDate
from Orders
group by EmployeeID
) t3
on t2.EmployeeID = t3.EmployeeID
where o.freight = t2.maxfreight and t2.orderdate < t3.mostRecentOrderDate
Step one is to read the order:
select o.employeeid, o.orderid, o.orderdate
from orders o
where o.orderid = #ParticularOrder;
That gives you everything you need to go out and get the previous orders from the same employee and join each one to the row you get from above.
select o.employeeid, o.orderid, o.orderdate, o2.freight
from orders o
join orders o2
on o2.employeeid = o.employeeid
and o2.orderdate < o.orderdate
where o.orderid = #ParticularOrder;
Now you have a whole bunch of rows with the first three values the same and the fourth is the freight cost of each previous order. So just group by the first three fields and select the maximum of the previous orders.
select o.employeeid, o.orderid, o.orderdate, max( o2.freight ) as maxfreight
from orders o
join orders o2
on o2.employeeid = o.employeeid
and o2.orderdate < o.orderdate
where o.orderid = #ParticularOrder
group by o.employeeid, o.orderid, o.orderdate;
Done. Build your query in stages and many times it will turn out to be much simpler than you at first thought.
It is unclear why you are using t3. From the question it doesn't sound like the employee's most recent order date is relevant at all, unless I am misunderstanding (which is absolutely possible).
I believe the issue lies in t2. You are grouping by orderdate, which will return the max freight for that date and employeeid, as you describe. You need to calculate a maximum total from all orders that occurred before the date that the order occurred on, for that employee, for every row you are returning.
It probably makes more sense to use a subquery for this.
SELECT o.employeeid, o.orderid, o.orderdate, m.maxfreight
FROM
orders o LEFT OUTER JOIN
(SELECT max(freight) as maxfreight
FROM orders AS f
WHERE f.orderdate <= o.orderdate AND f.employeeid = o.employeeid
) AS m
Hoping this is syntactically correct as I'm not in front of SSMS right now. I also included a left outer join as your previous query with an inner join would have excluded any rows where an employee had no previous orders (i.e. first order ever).
You can do what you want with a correlated subquery or apply. Here is one way:
select o.employeeid, o.orderid, o.orderdate, t2.maxfreight
from orders o outer apply
(select max(freight) as maxfreight
from orders o2
where o2.employeeid = o.employeid and
o2.orderdate < o.orderdate
) t2;
In SQL Server 2012+, you can also do this with a cumulative maximum:
select o.employeeid, o.orderid, o.orderdate,
max(freight) over (partition by employeeid
order by o.orderdate rows between unbounded preceding and 1 preceding
) as maxfreight
from orders o;