Is there a way to do FIRST() in SQL Server? - sql-server

From my old Access days, there was a First() function that allowed you to get the first row as an aggregate function. Is there any equivalent in SQL Server?
SELECT
c.ID
, p.ID
, FIRST(p.ProductName)
, SUM(fee.Amount)
from Fee as f
INNER JOIN Product as p
ON p.ID = f.ProductID
INNER JOIN Customer as c
ON c.ID = p.CustomerID
GROUP BY c.ID, p.ID
Edit:
I just wanted a value from any row, since they are all going to be the same. I was trying to be nice to the database and let it just give me the first one that it finds :)

Well, it depends.
You mean "any single row"? Then you can use MIN or MAX, it should work with most data types.
However, if you mean "the first row you can find", then the answer is no.
That's akin to telling the database engine that "I want you to give me a particular row, that fits these criteria, and one of the criteria is that you can give me any row you want".
The reason for this is that unless you order the rows, there's no concept of first, and you can't order the rows in any meaningful way that would work with a group this way.

You could try:
SELECT c.ID, p.ID,
(SELECT TOP 1 ProductName FROM Product ORDER BY ID) AS ProductName,
SUM(fee.Amount)
FROM Fee as f
INNER JOIN Product as pON p.ID = f.ProductID
INNER JOIN Customer as cON c.ID = p.CustomerIDGROUP BY c.ID, p.ID
This one gets the first product directly from the Product Table as a sub-query. The ORDER BY ID in the sub query should get you the first ProductName in the Product table.

You can do SELECT TOP 1 * FROM ... to get only the first row.

not that I know about, just use MIN()

Related

SQL Project using a where clause

So this is what I am working with new to sql and still learning been stuck on this for a few days now. Any advice would be appreciated I attached the image of the goal I'm trying to achieve
OrderItem And Product Table
Order And OrderItem Table(https://i.stack.imgur.com/pdbMT.png)
Scenario: Our boss would like to see the OrderNumber, OrderDate, Product Name, UnitPrice and Quantity for products that have TotalAmounts larger than the average
Create a query with a subquery in the WHERE clause. OrderNumber, OrderDate and TotalAmount come from the Order table. ProductName comes from the Product table. UnitPrice and Quantity come from the OrderItem table.
This is the code I came up with but it causes product name to run endlessly and displays wrong info.
USE TestCorp;
SELECT DISTINCT OrderNumber,
OrderDate,
ProductName,
i.UnitPrice,
Quantity,
TotalAmount
FROM [Order], Product
JOIN OrderItem i ON Product.UnitPrice = i.UnitPrice
WHERE TotalAmount < ( SELECT AVG(TotalAmount)
FROM [Order]
)
ORDER BY TotalAmount DESC;
Best guess assuming joins and fields not provided.
SELECT O.OrderNumber, O.orderDate, P.ProductName, OI.UnitPrice, OI.Quantity, O.TotalAmount
FROM [Order] O
INNER JOIN OrderItem OI
on O.ID = OI.orderID
INNER JOIN Product P
on P.ID= OI.ProductID
CROSS JOIN (SELECT avg(TotalAmount) AvgTotalAmount FROM [Order]) z
WHERE O.TotalAmount > z.AvgTotalAmount
Notes:
You're mixing join notations don't use , and inner join together that's mixing something called ANSI Standards.
I'm not sure why you have a cross join to product to begin with
You don't specify how to join Order to order item.
It seems very odd to be joining on Price.... join on order ID or productID maybe?
you could cross join to an "Average" result so it's available on every record. (I aliased this inline view "Z" in my attempt)
so what the above does is include all Orders. and for each order, an order item must be associated for it to be included. And then for each order item, a productid must be included and related to a record in product. If for some reason an order item record doens't have a related entry in product table, it gets excluded.
I use a cross join to get the average as it's executed 1 time and applied/joined to every record.
If we use the query in the where clause it's executed one time for EVERY record (unless the DB Engine optimizer figures it out and generates a better plan)
I Assume
Order.ID relates to OrderItem.OrderID
OrderItem.productID relates to Product.ID
Order.TotalAmount is what we are wanting to "Average" and compare against
Every Order has an Order Item entry
Every Order Item entry has a related product.

Summing a total from multiple sales into a single column

I'm having issues with what I believe should be a simple problem in SQL Server 2017. I need to show the total sales price for a given sale. This is what I have written (note I deleted the totalprice sum formulae because it returned an error) :
USE [Antiques]
GO
DECLARE #TotalPrice INT
SELECT
Sales.SaleNo,
Sales.SalesDate,
Customers.FirstName,
Customers.LastName,
Products.Price,
#TotalPrice AS TotalPrice
FROM
Customers JOIN Sales ON Customers.CustomerID = Sales.CustomerID
JOIN SalesProducts ON Sales.SaleNo = SalesProducts.SaleNo
JOIN Products ON SalesProducts.ProductID = Products.ProductID
WHERE (Products.ProductID = SalesProducts.ProductID)
GO
This is the result:
Even if I remove the item price (which I have just put in temporarily and won't be in final code) there are still multiple lines per sale (1 for each item in the sale). Have I used the wrong joins? How can I get it looking like this:
Can anyone please tell me what I'm doing wrong? I've tried so many ways of using the Sum function and Group By and failing. (Note: this has to be a basic query and not a stored procedure unfortunately)
Actually you're not too far away. I'd move the grouping in a derived table and join that, to get the totalprice.
SELECT sales.saleno,
sales.salesdate,
customers.firstname,
customers.lastname,
x.totalprice
FROM sales
INNER JOIN customers
ON customers.customerid = sales.customerid
INNER JOIN (SELECT salesproducts.saleno,
sum(products.price) totalprice
FROM salesproducts
INNER JOIN products
ON products.productid = salesproducts.productid
GROUP BY salesproducts.saleno) x
ON x.saleno = sales.salesno;

How to show last data entry of tables are not directly related

So I've read the posts about retrieving the last entry added to a table, but I can't get it to work for me since my query is slightly more complex.
I'm using SQL server and I'm a beginner with SQL queries.
I need to make a list with patients of a certain practice and their last consult. For this I have three tables:
Patients.patients (patient data, also contains practice id)
Consults.consults (contains no patient data!)
Consults.Patients (links consult is to patient id)
This is what I have got so far:
SELECT p.DebtorNumber
,p.ContactDetails_LastName
,c.Startdate
,c.ConsultNumber
FROM Patients.patients as p
JOIN consults.patients as pc on p.id=pc.Patient_Id
JOIN consults.Consults as c on pc.Parent_Id=c.id
WHERE p.HealthCare_Id=90
I use regular JOINS because I'm only interested in patients that have consults. This query retrieves all the consults of the patients in practice id 90, but I only need the most recent one.
As I have read other posts about this issue, I know that I should use a subquery and a TOP 1. But in the other examples the table they pulled the TOP 1 from was directly related to the main table.
I think I need to add to my WHERE, but this sure doesn't work:
and c.id in (select top 1 c.id where pc.Patient_Id=p.id order by start DESC)
How do I only get the last consult for each patient? This would be the consult with the highest Consults.Consults.Startdate.
Thanks in advance guys, I've gotten in over my head here.
Use ROW_NUMBER for this. EG
with q as
(
SELECT p.DebtorNumber
,p.ContactDetails_LastName
,c.Startdate
,c.ConsultNumber
,row_number() over (partition by p.id order p c.start desc) rn
FROM Patients.patients as p
JOIN consults.patients as pc on p.id=pc.Patient_Id
JOIN consults.Consults as c on pc.Parent_Id=c.id
WHERE p.HealthCare_Id=90
)
SELECT p.DebtorNumber
,p.ContactDetails_LastName
,c.Startdate
,c.ConsultNumber
FROM q
where rn = 1;
you can use query as below:
SELECT top (1) with ties
p.DebtorNumber
,p.ContactDetails_LastName
,c.Startdate
,c.ConsultNumber
FROM Patients.patients as p
JOIN consults.patients as pc on p.id=pc.Patient_Id
JOIN consults.Consults as c on pc.Parent_Id=c.id
WHERE p.HealthCare_Id=90
Order by row_number() over (partition by p.id order by c.start desc)

SQL: Summing columns with a similar column in common

I'm extremely new to SQL Sever and so I apologize if the question is worded strange. I am doing a homework assignment, and this is the question:
"A manager wants to know the email address, number or orders, and the total amount of purchases made by each customer. Create a summary query that returns these three items for each customer that has orders."
I have all of the data queried, the problem is when I pull data from each customer, it will show the quantity of items per order, and I need the items to be pooled together into one column. This is my query thus far (again, total noob, please excuse any poor syntax, etc.)
SELECT EmailAddress,
ItemPrice - DiscountAmount * Quantity AS TotalPurchaseAmount,
COUNT(*) AS OrderQty
FROM Customers
JOIN Orders ON Customers.CustomerID = Orders.CustomerID
JOIN OrderItems ON Orders.OrderID = OrderItems.OrderID
GROUP BY Orders.CustomerID,
OrderItems.ItemPrice, OrderItems.DiscountAmount,
OrderItems.Quantity,
Customers.EmailAddress;
The following is a small bit of the result set that I get:
Email Address OrderTotal OrderQty
allan.sherwood#yahoo.com 253.15 2
allan.sherwood#yahoo.com 839.30 2
allan.sherwood#yahoo.com 1208.16 2
barryz#gmail.com 303.79 4
christineb#solarone.com 479.60 2
david.goldstein#hotmail.com 299.00 2
david.goldstein#hotmail.com 489.30 1
david.goldstein#hotmail.com 479.60 1
So as you can see, I have several orders I need to smoosh together into one single row per e-mail, I have looked and looked for an answer but the only thing I can find is how to find duplicates and ignore them, not combine their data. Any help is extremely appreciate, thanks so much for taking the time to read this :) If my question doesn't make sense please let me know so I can clear up any bad wording I may have used!
Just do GROUP BY CustomerID, EmailAddress:
SELECT
c.EmailAddress,
SUM((i.ItemPrice - i.DiscountAmount) * Quantity) AS TotalPurchaseAmount,
COUNT(*) AS OrderQty
FROM Customers c
INNER JOIN Orders o
ON c.CustomerID = o.CustomerID
INNER JOIN OrderItems i
ON o.OrderID = i.OrderID
GROUP BY
c.CustomerID, c.EmailAddress
Additional note: Use aliases for your tables
You need to change your formula and remove columns that you dont want to group by from select query..
for example your query should be something like this
SELECT EmailAddress,
--do your aggregation here
blah AS TotalPurchaseAmount,
COUNT(*) AS OrderQty
FROM Customers
JOIN Orders ON Customers.CustomerID = Orders.CustomerID
JOIN OrderItems ON Orders.OrderID = OrderItems.OrderID
GROUP BY Orders.CustomerID,
Customers.EmailAddress;

Selecting Records From 1 Table That Don't Appear In Another Table

I am hoping someone might be able to help me with this issue I am having.
I have a table of customers - let's call it Table C. And I have a second table of customers that are not to be called - let's call it Table D.
I would like to pull all of the needed information (name, address, phone, etc...) from Table C unless the customer appears in Table D.
In the example shown below, I'd like to have data returned for all customers except John Doe (ID: 1) and Fred Savage (ID: 5)
I think a RIGHT OUTER JOIN might be applicable here, but I have not used this type of join before.
Use NOT EXISTS to do this:
SELECT c.*
FROM tableC c
WHERE NOT EXISTS (
SELECT *
FROM tableD d
WHERE c.customerID = d.customerid
);
If you want to use a join then it's a left join you want with a filter for null values in the d table. A right join would have gotten you all the rows from the d table, plus the matching ones from the c table - quite the opposite of what you want, but if you had switched the tables around then you would have gotten the same result, so this:
select c.* from c
left join d on c.CustomerID = d.CustomerID
where d.CustomerID is null
is equivalent to:
select c.* from d
right join c on c.CustomerID = d.CustomerID
where d.CustomerID is null;
Personally I would prefer using either a correlated not exists query or not in (but beware of null values) as I think those convey the intent more clearly.
Select * from table.c where customer_id not in (select distinct customer_id from table.d);
Yes you want an outer join.
Try this: https://technet.microsoft.com/en-US/library/ms187518(v=SQL.105).aspx

Resources