Trouble with COUNT - sql

I have two tables, PATIENT and VISIT. One with PatientID as the primary key and one with VisitID as the primary key. I need to select the first name and last name of the patients that have visited the hospital more than twice.
I have tried DISTINCT, a nested where clause, INNER JOIN, etc.
SELECT FirstName
, LastName
, PatientID
, COUNT(*) AS total_visits
FROM VISIT
WHERE total_visits > 2;
It should just show the first and last name of the patients that have more than two occurrences in the VISIT table, but no matter how I rearrange the code it doesn't work.

Following on from Gordon's answer and your comment I presume that PatientID in VISIT is a key to the PATIENT table. So you will need to use an ´INNER JOIN´. So your query looks something like this:
SELECT FirstName, LastName, v.PatientID, COUNT(*) AS total_visits
FROM VISIT v
INNER JOIN PATIENT p ON p.PatientID = v.PatientID
GROUP BY FirstName, LastName, v.PatientID
HAVING COUNT(*) > 2;
Note that AFAIK in Access you cannot use the alias name in the HAVING clause. You need to repeat the COUNT(*) as is.

You need GROUP BY and HAVING:
SELECT FirstName, LastName, PatientID, COUNT(*) AS total_visits
FROM VISIT
GROUP BY FirstName, LastName, PatientID
HAVING total_visits > 2;

Related

SQL Query to Obtain the Oldest People

I am trying to find the oldest customers in my database. I want just their full names and their ages, but my current results are outputting all customers and their ages (not just the oldest). What am I doing wrong here?
SELECT
LTRIM(CONCAT(' ' + Prefix, ' ' + FirstName,
' ' + MiddleName, ' ' + LastName, ', ' + Suffix)),
MAX(DATEDIFF(year, BirthDate, GETDATE()))
FROM
Customers
WHERE
BirthDate is not null
GROUP BY
Prefix, FirstName, MiddleName, LastName, Suffix
ORDER BY
MAX(DATEDIFF(year, e.BirthDate, GETDATE())) desc
Note that there seems to be multiple customers with the same oldest age.
You have not defined what you mean with "oldest customers".
So I will give a few options you could try
to see a list of customers with the oldest on top, use a simple querie like this
SELECT FirstName, LastName, Suffix, BirthDate
FROM Customers
WHERE BirthDate is not null
ORDER BY BirthDate desc
to restrict the result to a number of rows, for example the 10 oldest, use top 10
SELECT top 10
FirstName, LastName, Suffix, BirthDate
FROM Customers
WHERE BirthDate is not null
ORDER BY BirthDate desc
to restrict the result to all customers born after a certain date, add to the where clause
SELECT FirstName, LastName, Suffix, BirthDate
FROM Customers
WHERE BirthDate is not null
and BirtDate < '19920101'
ORDER BY BirthDate desc
The first thing you need to do before you do anything else is define a unique numeric primary key on the Customers table.
ALTER TABLE Customers ADD Cust_Id int IDENTITY(1,1);
ALTER TABLE Customers ADD CONSTRAINT PK_Customers PRIMARY KEY (Cust_Id);
After you've doe that, the following code will give you the "oldest customer (or customers) in your database".
With qry1 As (
SELECT Cust_Id,
DATEDIFF(year, BirthDate, GETDATE()) As Age
FROM Customers
WHERE BirthDate is not null
),
qry2 As (
SELECT Max(Age) As Max_Age
FROM qry1
)
SELECT Customers.Cust_Id,
Customers.Prefix,
Customers.FirstName,
Customers.MiddleName,
Customers.LastName,
Customers.Suffix,
Qry1.Age
FROM Customers
Inner Join Qry1 On Customers.Cust_Id = Qry1.Cust_Id
Inner Join Qry2 On Qry1.Age = Qry2.Max_Age

No column was specified for column 1 of 'T1' when using a sub-select with a group by

I have a working query:
SELECT
COUNT(*), ACCOUNT_ID
FROM
CDS_PLAYER
GROUP BY
ACCOUNT_ID
HAVING
COUNT(*) > 1`
Output
No column name Account_ID
----------------------------
'2' '12345'
I'm trying to add names to these accounts (all from the same table) but with no luck. The only query that gets me close is:
SELECT
LASTNAME, FIRSTNAME, COUNT(ACCOUNT_ID) AS NUMBER
FROM
(SELECT
COUNT(*), ACCOUNT_ID
FROM
CDS_PLAYER
GROUP BY
ACCOUNT_ID
HAVING
COUNT(*) > 1) AS T1
GROUP BY
LASTNAME, FIRSTNAME, PLAYER_ID
But I get an error:
No column was specified for column 1 of 'T1'
Like I said VERY NEW AT THIS. My boss of 4 months wanted me to learn and so I'm self taught (books and google). Any help at all to get me where I need to be would be appreciated!
(I'm using Windows Server 2003 and SQL Server 2000)
The error message can be resolved as below
SELECT LASTNAME, FIRSTNAME, COUNT(ACCOUNT_ID) AS NUMBER
FROM
(SELECT COUNT(*) AS Total, ACCOUNT_ID FROM CDS_PLAYER GROUP BY ACCOUNT_ID HAVING
COUNT(*) > 1) AS T1
GROUP BY LASTNAME, FIRSTNAME, PLAYER_ID`
Add as TOTAL after the count(*)
Does this do what you want?
SELECT COUNT(*), ACCOUNT_ID, LASTNAME, FIRSTNAME, PLAYER_ID
FROM CDS_PLAYER
GROUP BY ACCOUNT_ID, LASTNAME, FIRSTNAME, PLAYER_ID
HAVING COUNT(*) > 1;
You should also update your version of SQL Server. It is like 15 years out of date and hasn't been supported in many years. You can download a free version of SQL Server Express from Microsoft.
you want to select the LASTNAME and FIRSTNAME, but havn't it selected in your subselect. You only can access field which are in the resultset.
Solution: Add it to your GROUP BY clause.
ie:
SELECT
LASTNAME, FIRSTNAME, COUNT(ACCOUNT_ID) AS NUMBER
FROM
(SELECT COUNT(*), LASTNAME, FIRSTNAME, ACCOUNT_ID
FROM CDS_PLAYER
GROUP BY ACCOUNT_ID, LASTNAME, FIRSTNAME
HAVING COUNT(*) > 1) AS T1
GROUP BY
LASTNAME, FIRSTNAME, PLAYER_ID

Deleting duplicates in a table based on a criteria only in SQL

Let's say I have a table with columns:
CustomerNumber
Lastname
Firstname
PurchaseDate
...and other columns that do not change anything in the question if they're not shown here.
In this table I could have many rows for the same customer with different purchase dates (I know, poorly designed... I'm only trying to fix an issue for reporting, not really trying to fix the root of the problem).
How, in SQL, can I keep one record per customer with the latest date, and delete the rest? A group by doesn't seem to be working for my case
;with a as
(
select row_number() over (partition by CustomerNumber, Lastname, Firstname order by PurchaseDate desc) rn
from <table>
)
delete from a where rn > 1
This worked for me (on DB2):
DELETE FROM my_table
WHERE (CustomerNumber, Lastname, Firstname, PurchaseDate)
NOT IN (
SELECT CustomerNumber, Lastname, Firstname, MAX(PurchaseDate)
FROM my_table
GROUP BY CustomerNumber, Lastname, FirstName
)
SELECT CustomerNumber, Lastname, Firstname, MAX(PurchaseDate) LatestPurchaseDate
FROM Table
GROUP BY CustomerNumber, Lastname, Firstname
The MAX will select the highest (latest) date and show that date for each unique combination of the GROUP BY columns.
EDIT: I misunderstood that you wanted to delete records for all but the latest purchase date.
WITH Keep AS
(
SELECT CustomerNumber, Lastname, Firstname, MAX(PurchaseDate) LatestPurchaseDate
FROM Table
GROUP BY CustomerNumber, Lastname, Firstname
)
DELETE FROM Table
WHERE NOT EXISTS
(
SELECT *
FROM Keep
WHERE Table.CustomerNumber = Keep.CustomerNumber
AND Table.Lastname = Keep.Lastname
AND Table.Firstname = Keep.Firstname
AND Table.PurchaseDate = Keep.LastPurchaseDate
)

In a SQL GROUP BY query, what value is used for the non-aggregate columns?

Say I've got the following data back from a SQL query:
Lastname Firstname Age
Anderson Jane 28
Anderson Lisa 22
Anderson Jack 37
If I want to know the age of the oldest person with the last name Anderson, I can select MAX(Age) and GROUP BY Lastname. But I also want to know the first name of that oldest person. How can I make sure that, when the Firstname values are collapsed into one row by the GROUP BY, I get the Firstname value from the same row where I got the max age?
For those RDBMS that support it (e.g., SQL Server 2005+), you can use a window function:
select t.Lastname, t.Firstname, t.Age
from (select Lastname, Firstname, Age,
row_number() over (partition by Lastname order by Age desc) as RowNum
from YourTable
) t
where t.RowNum = 1
For others, you'd need a subquery on Lastname and a join to get Firstname:
select yt.Lastname, yt.Firstname, yt.Age
from YourTable yt
inner join (select LastName, max(Age) as MaxAge
from YourTable
group by LastName) q
on yt.Lastname = q.Lastname
and yt.Age = q.MaxAge
You have to join back to the table from your grouped results - i.e. create a view or a nested query to contain the group by.
The main thing you need to watch out for whatever your approach is that there might be more than 1 firstname with the same age for a given lastname.
This query will return just 1 row, but if your data set had more than one 'Anderson' aged 37, it could return either one:
select firstname, age
from yourtable
where lastname = 'Anderson'
order by age desc limit 1

MySQL, return only rows where there are duplicates among two columns

I have a table in MySQL of contact information ;
first name, last name, address, etc.
I would like to run a query on this table that will return only rows with first and last name combinations which appear in the table more than once.
I do not want to group the "duplicates" (which may only be duplicates of the first and last name, but not other information like address or birthdate) -
I want to return all the "duplicate" rows so I can look over the results and determine if they are dupes or not. This seemed like it would be a simple thing to do, but it has not been.
Every solution I can find either groups the dupes and gives me a count only (which is not useful for what I need to do with the results) or doesn't work at all.
Is this kind of logic even possible in a query ? Should I try and do this in Python or something?
You should be able doing this with the GROUP BY approach in a sub-query.
SELECT t.first_name, t.last_name, t.address
FROM your_table t
JOIN ( SELECT first_name, last_name
FROM your_table
GROUP BY first_name, last_name
HAVING COUNT(*) > 1
) t2
ON ( t.first_name = t2.first_name, t.last_name = t2.last_name )
The sub-query returns all names (first_name and last_name) that exist more than once, and the JOIN returns all records that match these names.
You could do it with a GROUP BY / HAVING and A SUB SELECT. Something like
SELECT t.*
FROM Table t INNER JOIN
(
SELECT FirstName, LastName
FROM Table
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1
) Dups ON t.FirstName = Dups.FirstName
AND t.LastName = Dups.LastName
select * from people
join (select firstName, lastName
from people
group by firstName, lastName
having count(*) > 1
) dupe
using (firstName, lastName)