SQL - How to group by and take first row of columns? - sql-server

I have the following SQL query
select SI.C_NUMBER as 'Invoice Number'
,SI.C_DATE as 'Date'
, CU.C_NAME as 'Customer'
, SI.C_ALTERNATEREFERENCE as 'Customer Order Number'
, SIL.C_NETAMOUNT as 'Items Net'
,MAN.C_NAME as 'Manufacturer'
from T_SALESINVOICE as SI WITH(NOLOCK)
inner join T_SALESINVOICE_LINE as SIL WITH(NOLOCK) on SIL.C__OWNER_ = SI.C_ID
inner join T_PRODUCT as PR WITH(NOLOCK) on PR.C_ID = SIL.C_PRODUCT
inner join T_MANUFACTURER as MAN WITH(NOLOCK) on MAN.C_ID = PR.C_MANUFACTURER
inner join T_CUSTOMER as CU WITH(NOLOCK) on CU.C_ID = SI.C_CUSTOMER
where CU.C_NAME = 'TEST CUSTOMER'
Which works perfectly, but I now need to Group by Invoice number, while taking the first result of Manufacturer, the reason for this is we have multiple item lines per invoice. Additionally, I need the Items Net to be the total of the 3 lines, while Manufacturer is only the first row.
So far I've tried;
select SI.C_NUMBER as 'Invoice Number'
,SI.C_DATE as 'Date'
, CU.C_NAME as 'Customer'
, SI.C_ALTERNATEREFERENCE as 'Customer Order Number'
, SUM(SIL.C_NETAMOUNT) as 'Items Net'
,MAN.C_NAME as 'Manufacturer'
from (
select SI.C_NUMBER
, SI.C_DATE
, CU.C_NAME
, SI.C_ALTERNATEREFERENCE
, SUM(SIL.C_NETAMOUNT)
, MAN.C_NAME
,row_number() over(partition by SI.C_NUMBER order by SI.C_DATE desc) as roworder
from T_SALESINVOICE as SI WITH(NOLOCK)
inner join T_SALESINVOICE_LINE as SIL WITH(NOLOCK) on SIL.C__OWNER_ = SI.C_ID
inner join T_PRODUCT as PR WITH(NOLOCK) on PR.C_ID = SIL.C_PRODUCT
inner join T_MANUFACTURER as MAN WITH(NOLOCK) on MAN.C_ID = PR.C_MANUFACTURER
inner join T_CUSTOMER as CU WITH(NOLOCK) on CU.C_ID = SI.C_CUSTOMER
) temp
where roworder = 1
and CU.C_NAME = 'TEST CUSTOMER'
But unfortunately gee the error "Column 'T_SALESINVOICE.C_NUMBER' is invalid in the select list because it is not contained in either an aggregate function of the GROUP BY clause."
I know almost nothing of SQL, started trying to mess with this today.
Edit:
Example of current result;
Invoice Number
Customer
Order Number
Items Net
Manufacturer
1234
Test
PO999
659.00
Dell
1234
Test
PO999
123.00
HP
1235
Test
PO998
451.00
Dell
1236
Test
PO997
871.00
Lenovo
1236
Test
PO997
253.00
Dell
Desired result;
Invoice Number
Customer
Order Number
Items Net
Manufacturer
1234
Test
PO999
782.00
Dell
1235
Test
PO998
451.00
Dell
1236
Test
PO997
1124.00
Lenovo
Hope this clears things up!

Related

Query performances by evaluating Execution Plan (Join First Row)

I have the following tables:
Customers
ID Name
============
1 John
2 Alice
3 Bob
Orders
ID CustomerID Status
==========================
1001 1 1
1002 2 1
1003 2 2
1004 3 2
I'd like to join tables showing one entry per customer only (the one with lowest Status) i.e.
ID Name OrderID
======================
1 John 1001
2 Alice 1002
3 Bob 1004
Thanks to the answer to this question, I chose 2 solutions which produce the same output:
Solution 1
SELECT c.id, c.name, o.id FROM customers AS c
INNER JOIN orders AS o ON
c.id = o.customerid
WHERE o.status = (SELECT MIN(status) FROM orders WHERE customerid = c.id)
Solution 2
SELECT c.id, c.name, o.id FROM customers as c
INNER JOIN orders AS o ON
o.id = (SELECT TOP 1 id FROM orders WHERE customerid = c.id ORDER BY status)
Trying to understand which one runs faster, I used SQL Fiddle View Execution Plan which gave the following:
Solution 1
Solution 2
How to interpret those diagrams and which one performs faster?
Using MS SQL Server 2016.
Here's my breakdown and the last one is my suggestion to you.
Query Cost 67%
SELECT c.id, c.name, o.id FROM #Customers AS c
INNER JOIN #Orders AS o ON
c.id = o.customerid
WHERE o.status = (SELECT MIN(status) FROM #Orders WHERE customerid = c.id)
Query Cost 66%
SELECT c.id, c.name, o.id FROM #Customers as c
INNER JOIN #Orders AS o ON
o.id = (SELECT TOP 1 id FROM #Orders WHERE customerid = c.id ORDER BY status)
Query Cost 47%
SELECT
x.CustID,
x.Name,
x.OrderID
FROM (SELECT
C.id CustID,
c.Name,
o.ID OrderID,
o.status,
ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY o.status) rn
FROM #Customers c
INNER JOIN #Orders o
ON o.CustomerID = c.ID) x
WHERE x.rn = 1

Group nested selected query returning all rows

I'm trying to display the data so that only one line is displayed per customer, i'm having trouble with trying to achieve that with my code as its returning all records, can anyone help
SELECT customerOrdrs.NAME AS 'Name',
customerOrdrs.currentbalance -
Sum(COALESCE(customerOrdrs.revisedbalance, 0)) AS 'RevisedBalance',
sold AS 'NumberOfItemsSold'
FROM customers,
(SELECT c.NAME AS NAME,
c.balance AS CurrentBalance,
i.qty AS RevisedBalance,
( Min(s.price) * i.qty ) AS Sold
FROM customers c
INNER JOIN sales o
ON c.NAME = o.custname
INNER JOIN purchases i
ON i.orderno = o.orderno
INNER JOIN contracters s
ON i.item = s.item
GROUP BY c.NAME,
c.balance,
i.qty) customerOrdrs
GROUP BY customerOrdrs.NAME,
customerOrdrs.currentbalance,
sold
I'm not sure how your data looks but I have reformatted the query and there are a few things I've noticed off the bat.
I have removed the subquery as I don't believe it is necessary - in addition your original query is referring to customer table twice without defining a join
Select [C].[Name] As [Name]
, [CurrentBalance] = [C].[Balance]
, [RevisedBalance] = [C].[Balance] - Sum([P].[Qty])
, [Sold] = ( Min([CO].[Price]) * sum([P].[Qty]) )
From [CUSTOMERS] [C]
Inner Join [Sales] [s]
On [C].[Name] = [s].[custName]
Inner Join [Purchases] [P]
On [P].[OrderNo] = [s].[OrderNo]
Inner Join [Contracters] [CO]
On [P].[Item] = [CO].[Item]
Group By [C].[Name]
, [C].[Balance];

only 1 value returns with sql query

AdventureWorks2012 DB - I am trying to return top 1 or 2 emlpoyees from Finance dept and Engineer dept who have worked longest. I cant get my query to return both, only results from engineering show. Any suggestions?
SELECT TOP 2 EDH.StartDate, E.BusinessEntityID, D.Name, EDH.EndDate, DATEDIFF(hour,EDH.StartDate, GETDATE()) AS HoursWorked
FROM HumanResources.Employee E
INNER JOIN Person.Person PP ON E.BusinessEntityID = PP.BusinessEntityID
INNER JOIN HumanResources.EmployeeDepartmentHistory EDH ON E.BusinessEntityID = EDH.BusinessEntityID
INNER JOIN HumanResources.Department D ON D.DepartmentID = EDH.DepartmentID
WHERE (D.Name LIKE 'Finance' OR D.Name = 'Engineering')
AND EDH.EndDate IS NULL
GROUP BY D.Name, EDH.StartDate,E.BusinessEntityID,EDH.EndDate
ORDER BY EDH.StartDate ASC
Your problem is that the employees from Engineering happen to have started before the employees from Finance. The ORDER BY is affecting all of your records (both departments), and then the TOP 2 value is grabbing the two most recent employees, regardless of departments.
If you are trying to write a query that returns the first employee from each department, you're going to have to get a bit more complex. Here is an example that uses the ROW_NUMBER() function to order employees within each department by their start date, then filters those records to only return employees who are the first individuals in their department.
SELECT
StartDate,
BusinessEntityID,
Name,
EndDate,
HoursWorked
FROM
(
SELECT
EDH.StartDate,
E.BusinessEntityID,
D.Name,
EDH.EndDate,
DATEDIFF(hour,EDH.StartDate, GETDATE()) AS HoursWorked,
ROW_NUMBER() OVER (PARTITION BY D.Name ORDER BY EDH.StartDate) AS RowNumberWithinDepartment
FROM
HumanResources.Employee E
INNER JOIN
Person.Person PP ON E.BusinessEntityID = PP.BusinessEntityID
INNER JOIN
HumanResources.EmployeeDepartmentHistory EDH ON E.BusinessEntityID = EDH.BusinessEntityID
INNER JOIN
HumanResources.Department D ON D.DepartmentID = EDH.DepartmentID
WHERE
(D.Name LIKE 'Finance' OR D.Name = 'Engineering') AND
EDH.EndDate IS NULL
GROUP BY D.Name, EDH.StartDate,E.BusinessEntityID,EDH.EndDate
) x
WHERE RowNumberWithinDepartment = 1
ORDER BY StartDate ASC

Error when using WHERE in Correlated Subquery

I have the following relationship: Bank -> Financing -> Contracts -> Supplier
I have to select all the Banks that is related to a Supplier, using the query below:
SELECT DISTINCT A.Name AS BankName, A2.Name as SupplierName
FROM Companies A
INNER JOIN Financing F ON F.IdFinancialCompany = A.Id
INNER JOIN Contracts C ON F.IdContract = C.Id
INNER JOIN Companies A2 ON C.IdSupplier = A2.Id
GROUP BY A.Name, A2.Name
So far, so good.
Now I have to list the number of contracts that are active and the number of total contracts, so I thought about including a subquery:
SELECT DISTINCT A.Name AS BankName, A2.Name as SupplierName,
(SELECT COUNT(C.Id)),
(SELECT COUNT(C.Id) WHERE C.ContractStatus = 1)
FROM Companies A
INNER JOIN Financing F ON F.IdFinancialCompany = A.Id
INNER JOIN Contracts C ON F.IdContract = C.Id
INNER JOIN Companies A2 ON C.IdSupplier = A2.Id
GROUP BY A.Name, A2.Name
Line 2 Works well, but I got na error in line 3:
Column 'Contracts.ContractStatus' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I expect the following result:
BANK NAME | SUPPLIER NAME | TOTAL CONTRACTS | ACTIVE CONTRACTS
Bank 1 | Supplier 1| 5 | 2
How can I achieve this? I'm using SQL Server 2014
Thanks in advance!
Remove the distinct since your group by and use CASE
SELECT A.Name AS BankName, A2.Name as SupplierName,
COUNT(C.Id),
SUM(CASE WHEN C.ContractStatus = 1 THEN 1 ELSE 0 END)
FROM Companies A
INNER JOIN Financing F ON F.IdFinancialCompany = A.Id
INNER JOIN Contracts C ON F.IdContract = C.Id
INNER JOIN Companies A2 ON C.IdSupplier = A2.Id
GROUP BY A.Name, A2.Name

need help to group my result

I am using sql server 2008 r2. here's my query.
SELECT TOP 25 A.*, U.Displayname AS UserName,
SU.Displayname AS SmoothieAuthorName,
S.Name AS SmoothieName, S.Id AS SmoothieId
FROM dbo.Activity AS A
LEFT JOIN dbo.[User] AS U ON A.UserId = U.Id
LEFT JOIN dbo.[User] AS SU ON A.SmoothieAuthorId = SU.Id
LEFT JOIN dbo.Smoothie AS S ON A.SmoothieId = S.Id
WHERE A.UserId = 2 --#UserId
AND A.UserId <> A.SmoothieAuthorId
ORDER BY CreatedDate DESC
following is my result. now, i need to group them by SmoothieId and CreatedDate (only group date part, ignore time). First two should only return one back, 3 to 5 should only return one back. not sure how to do it, please help.
Id ActionType UserId SmoothieId SmoothieAuthorId CreatedDate UserName SmoothieAuthorName SmoothieName SmoothieId
1 view 2 128 1 2013-01-15 20:05:03.403 mike test1234 new testing 2d 128
2 view 2 128 1 2013-01-15 20:16:24.733 mike test1234 new testing 2d 128
12 view 2 128 1 2013-01-16 21:45:56.167 mike test1234 new testing 2d 128
13 view 2 128 1 2013-01-16 22:12:51.217 mike test1234 new testing 2d 128
14 view 2 128 1 2013-01-16 22:12:54.407 mike test1234 new testing 2d 128
15 view 2 69 1 2013-01-16 22:19:54.783 mike test1234 sdfsdfwww 69
If you need ALL columns from A including Id, I think you'll have a have a hard time including Id. I think you'll need to explicitly list the columns from A you are after.
I've also assumed you want a count of records you are grouping, hence the Count(TheDate) element.
Other than that, look at getting just the date portion of a datetime and group on that.
Something like;
SELECT ActionType, UserId, SmoothieId, SmoothieAuthorId,
TheDate, Count(TheDate) AS Occurances, UserName, SmoothieAuthorName,
SmoothieName, SmoothieId
FROM (
SELECT TOP 25 A.ActionType, A.UserId, A.SmoothieId,
A.SmoothieAuthorId,
DATEADD(dd, 0, DATEDIFF(dd, 0, CreatedDate)) AS TheDate,
U.Displayname AS UserName,
SU.Displayname AS SmoothieAuthorName,
S.Name AS SmoothieName, S.Id AS SmoothieId
FROM dbo.Activity AS A
LEFT JOIN dbo.[User] AS U ON A.UserId = U.Id
LEFT JOIN dbo.[User] AS SU ON A.SmoothieAuthorId = SU.Id
LEFT JOIN dbo.Smoothie AS S ON A.SmoothieId = S.Id
WHERE A.UserId = 2 --#UserId
AND A.UserId <> A.SmoothieAuthorId
-- ORDER BY CreatedDate DESC
) x GROUP BY ActionType, UserId, SmoothieId, SmoothieAuthorId, UserName,
TheDate, SmoothieAuthorName, SmoothieName, SmoothieId
ORDER BY The Date DESC
Note This isn't tested, it is just a quick suggestion at what I'd try.
I'm not sure I fully understand the question. Assuming you want distinct values for those columns, then just use DISTINCT:
SELECT DISTINCT
CAST(CreatedDate to Date) as DateWanted,
SmoothieId
FROM
(
SELECT TOP 25 A.*, U.Displayname AS UserName,
SU.Displayname AS SmoothieAuthorName,
S.Name AS SmoothieName, S.Id AS SmoothieId
FROM dbo.Activity AS A
LEFT JOIN dbo.[User] AS U ON A.UserId = U.Id
LEFT JOIN dbo.[User] AS SU ON A.SmoothieAuthorId = SU.Id
LEFT JOIN dbo.Smoothie AS S ON A.SmoothieId = S.Id
WHERE A.UserId = 2 --#UserId
AND A.UserId <> A.SmoothieAuthorId
) t
Reviewing your comment, you say you need all fields in the Activity table. What do you expect to receive in your Id column for the first 2 records, 1 or 2? Are all the other values in the other columns the same? To just get a single record, you're going to need to either pick one row over the other, or do a group by with the columns that have the same information.
Good luck.

Resources