How to filter a subquery? - sql-server

This is based on AdventureWorks sample database.
And here is my query:
SELECT FirstName, LastName,
(SELECT COUNT(SalesOrderID)
FROM SalesOrderHeader
WHERE SalesOrderHeader.ContactID = Contact.ContactID) AS OrderCount
FROM Contact
ORDER BY OrderCount desc
Question: what should I add to my query so it only shows OrderCount that is more than 20? Nothing else should change about my output. I tried this and it did not work:
WHERE SalesOrderHeader.ContactID = Contact.ContactID AS OrderCount AND COUNT(SalesOrderID) > 20

Rewrite as a join aggregation query and then use HAVING:
SELECT
c.FirstName,
c.LastName,
COUNT(s.SalesOrderID) AD OrderCount
FROM Contacts c
INNER JOIN SalesOrderHeader s
ON c.ContactID = s.ContactID
GROUP BY
c.FirstName,
c.LastName
HAVING
COUNT(s.SalesOrderID) > 20;

Related

Wrong count result

I have 2 tables in sql server:
Employee (Job Title, Hire Date)
Person (FirstName, LastName)
And I need to return the E.JobTitle, E.HireDate,P.FirstName ,P.LastName along with the how many employees have the same job title.
so I used this query:
SELECT E.JobTitle,
E.HireDate,
P.FirstName,
P.LastName,
COUNT(E.JobTitle)
FROM AdventureWorks2019.HumanResources.Employee E
JOIN AdventureWorks2019.Person.Person P ON E.BusinessEntityID = P.BusinessEntityID
GROUP BY E.JobTitle,
E.HireDate,
P.FirstName,
P.LastName;
the problem is that the query returns 1 per the count, while I'd expect to get per each row the count of the num employees with that job title.
my question is how can I get the correct count?
As mentioned by #Larnu in the comments, your issue is that you're grouping by LastName also, so you are going to get a count of 1 per LastName.
You need a windowed count with OVER, not an aggregated one with GROUP BY
No extra joins or grouping needed.
SELECT E.JobTitle,
E.HireDate,
P.FirstName,
P.LastName,
COUNT(*) OVER (PARTITION BY E.JobTitle)
FROM AdventureWorks2019.HumanResources.Employee E
JOIN AdventureWorks2019.Person.Person P ON E.BusinessEntityID = P.BusinessEntityID;
You should count the number of employee for each job title first in a subquery or cte.
SELECT
JobTitle,
COUNT(*) AS Count
FROM HumanResources.Employee
GROUP BY JobTitle
Then you can join the subquery or the cte with your original query.
WITH cte
AS
(
SELECT
JobTitle,
COUNT(*) AS Count
FROM HumanResources.Employee
GROUP BY JobTitle
)
SELECT
E.JobTitle,
E.HireDate,
P.FirstName,
P.LastName,
cte.Count
FROM
HumanResources.Employee E
INNER JOIN Person.Person P ON P.BusinessEntityID = E.BusinessEntityID
INNER JOIN cte on cte.JobTitle = E.JobTitle
ORDER BY
E.JobTitle,
P.LastName,
P.FirstName
;

Select columns from several tables with count

I have 3 tables in SQL Server:
Sales (customerId)
Customer (customerId, personId)
Person (personId, firstName, lastName)
and I need to return the top 10 customers.
I used this query:
SELECT TOP 10
CustomerID, COUNT(CustomerID)
FROM
Sales
GROUP BY
(CustomerID)
ORDER BY
COUNT(CustomerID) DESC
The query currently returns only the customerId and count, but I also need to return the firstName and lastName of these customers from the Person table.
I know I need to reach the firstName and lastName by correlating between Sales.customerId and Customer.customerId, and from Customer.personId to get the Person.personId.
My question is whether I need to use an inner join or union, and how to use either of them to get the firstName and lastName of these customers
Union is mostly used for disjoint sets. To achieve your target, u can go with inner-join.
If you want to use joins, then here is the query which works similarly to your requirement.
SELECT TOP 10 S.CustomerID, P.FirstName,P.LastName, count(*)
FROM Sales S
INNER JOIN Customer C on S.CustomerId=C.CustomerId
INNER JOIN Person P on C.PersonId = P.PersonId
GROUP BY (S.CustomerID, P.FirstName,P.LastName)
ORDER BY count(*) DESC
You need use inner join like this :
SELECT TOP 10 S.CustomerID
, P.FirstName
, P.LastName
, COUNT (1) AS CountOfCustomer -- this is equal count(*)
FROM Sales S
INNER JOIN Customer C ON S.CustomerId = C.CustomerId
INNER JOIN Person P ON C.PersonId = P.PersonId
GROUP BY S.CustomerID, P.FirstName, P.LastName
ORDER BY 4 DESC; -- this is equal order by count(*)

subquery to retrieve a query

My Sales.Customer table includes the following columns:
CustomerID (integer)
FirstName (nvarchar(50))
LastName (nvarchar(50))
My Sales.SalesOrder table includes the following columns:
SalesOrderNumber (integer)
OrderDate (date)
CustomerID (integer)
Amount (money)
Some customers have placed multiple orders over a period of years. I've written the following query to retrieve the last date on which each customer placed an order:
SELECT c.CustomerID, c.FirstName, c.LastName,
-- correlated subquery goes here
AS LastOrderDate
FROM Sales.Customer AS c;
Why does my subquery not complete my actual query? Am I missing something or should it be something different?
(SELECT MAX(o.OrderDate)
FROM Sales.SalesOrder AS o
WHERE o.CustomerID = c.CustomerID)
I considered whether I might be over-complicating my solution so maybe this should work instead?
(SELECT MAX(c.OrderDate)
FROM Sales.Customer AS c)
You should be able to rewrite it as a CTE like this.
;WITH LastOrderDate AS
(
SELECT CustomerID,MAX(OrderDate) AS LastOrderDate
FROM Sales.SalesOrder
GROUP BY CustomerID
)
SELECT c.CustomerID, c.FirstName, c.LastName, LastOrderDate
AS LastOrderDate
FROM Sales.Customer AS c
LEFT JOIN LastOrderDate l
ON c.CustomerID = l.CustomerID;

T-SQL Grouping Issue

I have 2 tables.
Contacts
ContactID pk
EmailAddress
FirstName
LastName
Address
Orders
OrderID pk
ContactID fk
I want to get the number or orders for each email address in Contacts like below
select
Contacts.EmailAddress,
count(distinct Orders.OrderID) AS NumOrders
from
Contacts inner join Orders on Contacts.ContactID = Orders.ContactID
group by
Contacts.EmailAddress
Problem is, I also want the first name, last name, address. But I can't group by those because each email address in Contacts could have a different first name, lastname or address associated with it.
ie:
myname#email.com, Fred, Jackson, 123 Main St
myname#email.com, Bob, Smith, 456 Spruce St.
How can I change my query so that I can get the first name, last name and address for the most recent entry made in Contacts for that email address?
Thanks in advance!
My first thought would be to use windowed functions.
SELECT EmailAddress,
FirstName,
Lastname,
[Address],
EmailOrderCount
FROM (SELECT c.EmailAddress,
c.FirstName,
c.LastName,
c.[Address],
COUNT(o.OrderID) OVER (PARTITION BY c.EmailAddress) EmailOrderCount,
ROW_NUMBER() OVER (PARTITION BY c.EmailAddress ORDER BY c.ContactID DESC) Rn
FROM Contacts c
JOIN Orders o ON c.ContactID = o.ContactID
) t
WHERE Rn = 1
Demo
another way would be to use CROSS APPLY to append the top 1 contact record to the summary rows.
SELECT c.EmailAddress,
COUNT(o.OrderID) NumOrders,
ca.FirstName,
ca.LastName,
ca.[Address]
FROM Contacts c
INNER JOIN Orders ON c.ContactId = o.ContactID
CROSS APPLY (
SELECT TOP 1
FirstName,
Lastname,
[Address]
FROM Contacts c2
WHERE c2.EmailAddress = c.EmailAddress
ORDER BY c2.ContactID DESC) ca
GROUP BY c.EmailAddress,
ca.FirstName,
ca.LastName,
ca.[Address]
Try this:
select
Contacts.Name,
Contacts.FirstName,
Contacts.LastName
Contacts.EmailAddress,
count(distinct Orders.OrderID) AS NumOrders
from
(
select max(ContactID) as ContactID,
EmailAddress
from Contacts
group by EmailAddress
) MinContactForEachEMailAddress
inner join
Contacts
on MinContactForEachEMailAddress.ContactID = Contacts.ContactID
inner join
Orders
on Contacts.ContactID = Orders.ContactID
group by
Contacts.EmailAddress
Another way to get what you want is using a CTE and taking the "maximum" row by using ROW_NUMBER.
;WITH CTE AS (
SELECT C.ContactId, C.Name, C.FirstName, C.LastName, C.EmailAddress,
ROW_NUMBER() OVER (PARTITION BY EmailAddress ORDER BY ContactId DESC) RowNo
FROM Contact C
)
SELECT CTE.*, COUNT(o.OrderID) OVER (PARTITION BY CTE.EmailAddress) Cnt
FROM CTE
JOIN Orders O on CTE.ContactID = O.ContactID
-- select the "maximum" row
WHERE CTE.RowNo = 1
An easy way to do this is to make your original query a subquery and select from it. I'm making a slight change, because it's a better practice to group by your primary key than your email address. (Is it a safe bet that each contact has just one email address, and that the basic intent is to group by person?) If so, try this:
SELECT DISTINCT c.EmailAddress, c.FirstName, c.LastName, c.Address, sub.NumOrders
FROM
(
select
Contacts.ContactID
count(distinct Orders.OrderID) AS NumOrders
from
Contacts inner join Orders on Contacts.ContactID = Orders.ContactID
group by
Contacts.ContactID
) sub
JOIN Contacts c
ON sub.ContactID = c.ContactID
If you really need to group by email address instead, then change the above subquery to your original query and change c.EmailAddress to sub.EmailAddress. Of course you may order the SELECT fields however best suits you.
Edit follows:
The ContactID must be a sequence number and you can continually put the same person in the table. So if you add the DISTINCT keyword in the outer query I believe that will give you what you need.

create sql query to fetch repeat column values within time frame

Can someone help me with this query? I want to get the result of all the customer_id which repeats more than once in 24hrs
SELECT
O.Order_No, O.Customer_ID, O.DateOrdered, O.IPAddress,
C.FirstName, C.LastName, CD.nameoncard
FROM
Order_No O
INNER JOIN
CardData CD ON O.card_id = CD.id
INNER JOIN
Customers C ON O.customer_id = C.customer_id
ORDER BY
O.order_no desc
adding more details..
so suppose order with customer id xx was placed on 04/23 2:30 pm and again 2nd order was placed with same customer Id xx on same day 04/23 5:30 pm.
i want the query to return me customer Id xx
Thanks
select Customer_ID, CAST(DateOrdered as Date) DateOrdered, count(*) QTDE
from Order_No
group by Customer_ID, CAST(DateOrdered as Date)
having count(*) > 1
To get the customers who have orders issued after the first one, then you could use the following query:
select distinct A.Customer_ID
from Order_No A
inner join (select Customer_ID, min(DateOrdered) DateOrdered from Order_No group by Customer_ID ) B
on A.Customer_ID = B.Customer_ID
and A.DateOrdered - B.DateOrdered <= 1
and A.DateOrdered > B.DateOrdered
SQL Fiddle
To get all customers that have ANY TIME more than one order issued in period less or equal than 24h
select distinct A.Customer_ID
from Order_No A
inner join Order_No B
on A.Customer_ID = B.Customer_ID
and A.DateOrdered > B.DateOrdered
and A.DateOrdered - B.DateOrdered <= 1
SQL Fiddle
Self-join:
SELECT distinct O.Customer_ID
FROM
Order_No O
inner join Order_No o2
on o.customerID = o2.customerID
and datediff(hour, o.DateOrdered, o2.DateOrdered) between 0 and 24
and o.Order_No <> o2.Order_No
This will return all customer_IDs that have ever placed more than one order in any 24 hour period.
Edited to add the join criteria that the matching records should not be the same record. Should return customers who placed two different orders at the same time, but not customers who placed only one order.

Resources