access SQL error with order by and group by - group-by

I am working on a homework exercise and I am ussing MS access 2013 and I am writing and sql that produces a query tha is supposed to Show the LastName and FirstName of all customers who have had an order with an Item named 'Dress Shirt'. Use a subquery. Present the results sorted by LastName, in ascending order and then FirstName in descending order. This is the code I wrote following what I have learned from the book
SELECT LastName, FirstName
FROM CUSTOMER, INVOICE_ITEM
WHERE Item In
(SELECT Item
FROM INVOICE_ITEM
WHERE Item="Dress Shirt")
GROUP BY LastName
ORDER BY FirstName DESC;
I get an error that my query does not include a specified expression 'FirstName' as part of an agregate funtion.

You have to group by with all the fields in select statement unless you count, sum, etc
Try:
SELECT LastName, FirstName
FROM CUSTOMER, INVOICE_ITEM
WHERE Item In (SELECT Item FROM INVOICE_ITEM WHERE Item="Dress Shirt") GROUP BY LastName, FirstName ORDER BY FirstName DESC;

Related

Creating query to reference prior row over grouped list of users

We have a collection of users with duplicates, and I'm writing a process to merge them. Basically selecting out all users with matching names and DOB's, then I need a list of user id's to merge them together. Here's an example:
CREATE TABLE #tmpUsers (UserID Integer NOT NULL PRIMARY KEY, FullName NVARCHAR(50), Birthdate DATE);
INSERT INTO #tmpUsers (UserID, FullName, Birthdate)
VALUES
(120,'John Michael','1985-03-02'),
(45,'John Michael','1985-03-02'),
(60,'John Michael','1985-03-02'),
(33,'John Michael','1985-03-02'),
(12,'Tim Smith','1973-01-02'),
(16,'Tim Smith','1973-01-02'),
(29,'Jane Thomas','1990-06-20'),
(43,'Jane Thomas','1990-06-20'),
(8,'Jane Thomas','1990-06-20');
The process I'm building needs to have a new table ordered by the Fullname and DOB, but have the current and prior ID so it can merge together, like this:
Name
DOB
Merge From
Merge To
Jane Thomas
1990-06-20
8
29
Jane Thomas
1990-06-20
29
43
John Michael
1985-03-02
33
45
John Michael
1985-03-02
45
60
John Michael
1985-03-02
60
120
Tim Smith
1973-01-02
12
16
The process basically merges or collapses the oldest values into the newest one, so in the end we will only have one User for each. I'm just unable to find any good way to do this, though I'm sure there's a simple TSQL method. I hoped someone had advise on how to build it.
In the end after my process runs it'll have three users with ID's 16, 43, 120. The others will either be removed or deactivated, but just getting the query to start the process is where I'm hung.
Thanks.
This will do it:
SELECT *
FROM (
SELECT FullName as Name, BirthDate as DOB, UserID as [Merge From],
LEAD(UserID) OVER(PARTITION BY fullname, birthdate
ORDER BY fullname, birthdate, userid) as [Merge To]
from #tmpUsers
) t
WHERE [Merge To] IS NOT NULL
ORDER BY Name, DOB, [Merge From];
See it work here:
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=c2557bc038cab44ab000a1b35ab1563b
This is ubiquitously solved with row_number to select the max value per group:
with u as (
select UserId, FullName, BirthDate,
Row_Number() over(partition by FullName order by UserId desc) keepMe
from #tmpUsers
)
select UserId, FullName, BirthDate
from u
where KeepMe=1
Though my suggestion follows a different approach to solve the underlying problem, why not run this simple query,
SELECT
MIN(UserID) AS MergeFrom,
MAX(UserID) AS MergeTo,
FullName,
BirthDate
FROM #tmpusers
GROUP BY
FullName,
BirthDate
HAVING MIN(UserID)<>MAX(UserID)
shift users as indicated and do this in a loop until the query returns an empty result set?

How do I make my RANK () OVER query work in select?

table image
I have this table that I need to sort in the following way:
need to rank Departments by Salary;
need to show if Salary = NULL - 'No data to be shown' message
need to add total salary paid to the department
need to count people in the department
SELECT RANK() OVER (
ORDER BY Salary DESC
)
,CASE
WHEN Salary IS NULL
THEN 'NO DATA TO BE SHOWN'
ELSE Salary
,Count(Fname)
,Total(Salary) FROM dbo.Employees
I get an error saying:
Column 'dbo.Employees.Salary' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Why so?
Column 'dbo.Employees.Salary' is invalid in the select list because it
is not contained in either an aggregate function or the GROUP BY
clause.
Why so?
The aggregate functions are returning a single value for the whole table, you can't SELECT a field alongside them it doesn't makes sense. Like say, you have a students table you apply Sum(marks) for the whole students table, and you are then also selecting student's name Select studentname in your query. Which student's name will the database engine select? Confusing
Column "invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause"
I tried this-
using inner query
SELECT RANK() OVER (ORDER BY SAL DESC) RANK,FNAME,DEPARTMENT
CASE
WHEN SAL IS NULL THEN 'NO DATA TO BE SHOWN'
ELSE SAL
END
FROM
(SELECT COUNT(FNAME) FNAME, SUM(SALARY) SAL, DEPARTMENT
FROM TESTEMPLOYEE
GROUP BY DEPARTMENT) t

psql, display column that is not in the group by clause

i'm having problems with a query. I have two tables: country and city and i want to display the city with the highest population per country.
Here's the query:
select country.name as coname, city.name as ciname, max(city.population) as pop
from city
join country on city.countrycode=country.code
group by country.name
order by pop;`
Error
column "city.name" must appear in the GROUP BY clause or be used in an aggregate function.
I don't know how to solve this, i tried to make a subquery but it didn't work out.
How can i make it work?
You can easly get it using rank function:
select * from
(
select country.name as coname,
city.name as ciname,
city.population,
rank() over (partition by country.name order by city.population desc) as ranking
from
city
join
country
on city.countrycode=country.code
) A
where ranking = 1

Removing duplicate data from the query with different condition

In the view I've created this query,
SELECT DISTINCT
ClientCode, ClientName, Address1, Address2, City, Country, CreatedBy, CreatedDate
FROM
Contact
GROUP BY
ClientCode, ClientName, Address1, Address2, City, Country, CreatedBy, CreatedDate
Which will give me this result,
001 ABC Lot No, Road, City B, US Alice 03/04/2012
001 ABC Lot No, Road, City B, US Benny 04/04/2012
How should I design my query so that I can filter out the duplicate data? I wanted to ignore the two fields - CreatedBy and CreatedDate by showing only 1 row of data. This is the result I wanna get.
001 ABC Lot No, Road, City B, US Alice 03/04/2012 !OR!
001 ABC Lot No, Road, City B, US Benny 04/04/2012
I want the query to have the ability to filter out duplicate data by comparing only the ClientCode, ClientName, Address1, Address2, City and Country. The reason of keeping the CreatedBy and CreatedDate is because I have to include it in another interface.
You can use a ranking function to get the most recent contact details for each address, based on the most recent date:
with recentContact as
(
select *
, mostRecentRank = row_number() over
(
partition by ClientCode
,ClientName
,Address1
,Address2
,City
,Country
order by CreatedDate desc
)
from contacts
)
select ClientCode
,ClientName
,Address1
,Address2
,City
,Country
,CreatedBy
,CreatedDate
from recentContact
where mostRecentRank = 1
SQL Fiddle with demo.
try to add
GROUP BY field
Where field should be any field that is equal like clientname
In your special case, you can add multiple fields to GROUP BY like
GROUP BY clientname, clientfoo ..

How do you perform a search on a 1-to-many relationship when the criteria could be on either table?

I am using t-sql. I have what I thought would be an easy search. There is a 1-to-many relationship between SalesPerson and TradeShow. 1 salesperson could have gone to many trade shows. I need to be able to search on the SalePerson. I also need to be able to search on the LAST trade show they attended. I thought I would be able to do simple join and group on their last trade show, but I can not display the City or State.
SELECT SalePersonID, FirstName, LastName, TradeShow.DateLastWent
FROM SalesPerson INNER JOIN
(SELECT SalePersonID, MAX(DateLastWent) AS DateLastWent
FROM TradeShow
GROUP BY SalesPersonID) AS TradeShow ON SalesPerson.SalePersonID= TradeShow.SalePersonID
This workds, but the Tradeshow also has city and State. I need to be able to search on and display city and state. But if I include them in the subquery, I have to include thm in an aggregate function, and if I do that, I get the incorrect city and state.
The tables are simple
SALEPERSON
salespersonID PK
firstname
lastname
TRADESHOW
tradeshowID PK
datelastwent
city
state
salespersonID FK
Re-word it: what you want is the salesperson, plus the information from the last show that they have been to.
Select
SalePersonID,
FirstName,
LastName,
TradeShow.DateLastWent,
TradeShow.City,
TradeShow.State
From
SalesPerson
Inner Join TradeShow
On SalesPerson.SalePersonID = TradeShow.SalePersonID
Where
TradeShow.TradeShowID =
(Select Top 1 Latest.TradeShowID
From TradeShow As Latest
Where SalesPerson.SalePersonID = Latest.SalePersonID
Order By Latest.DateLastWent Desc)
You can join TradeShow twice :
SELECT SalePersonID, FirstName, LastName, TS1.DateLastWent,
TS2.City, TS2.State
FROM SalesPerson INNER JOIN
(SELECT SalePersonID, MAX(DateLastWent) AS DateLastWent
FROM TradeShow
GROUP BY SalesPersonID
) AS TS1 ON (SalesPerson.SalePersonID= TradeShow.SalePersonID)
INNER JOIN TradeShow TS2 ON
(TS2.SalePersonID = TS1.SalePersonID AND TS2.DateLastWent = TS1.DateLastWent)
WHERE TS2.City = 'CityName'
There is likely a more elegant way to solve this, but my first thought is to simply grab the newest TradeShow record to join with
SELECT SalePersonID, FirstName, LastName, TradeShow.DateLastWent
FROM SalesPerson
INNER JOIN (
SELECT *
FROM (
SELECT TradeShowId, DateLastWent, City, State, SalesPersonId
FROM TradeShow
ORDER BY datelastwent DESC
)
WHERE ROWNUM <= 1
) ON SalesPerson.SalesPersonId = TradeShow.SalesPersonId
Edit
Oops... been playing with Oracle too much
ROW_NUMBER() OVER(order by date) or SELECT TOP X
would be thw SQL Server way for doing this... don't have an instance of SQL-Server running, but pretty sure the syntax ends up being something like
SELECT SalePersonID, FirstName, LastName, TradeShow.DateLastWent
FROM SalesPerson
INNER JOIN (
SELECT TradeShowId, DateLastWent, City, State, SalesPersonId, ROW_NUMBER() OVER(PARTITION BY TradeShow.SalesPersonId ORDER BY DateLastWent DESC) RowNumber
FROM TradeShow
) ON SalesPerson.SalesPersonId = TradeShow.SalesPersonId AN TradeShow.RowNumber = 1