I have this two SQL tables:
create table Kids (
id Int Identity (1,1) primary key not null
, firstName varchar(100)
, lastName varchar(100)
, birthDate datetime
)
create table Toys (
id Int Identity (1,1) primary key not null
, kidId Int
, toyName varchar(100)
, colour varchar(100)
)
ALTER TABLE Toys
ADD FOREIGN KEY (kidId) REFERENCES Kids(id);
I am trying to get: all kids under 5 years of age who have more than 1 toy
I have this query:
select CONCAT(Kids.firstName,' ',Kids.lastName) as FullName,
birthDate,
count(Toys.Id) as 'Number of Toys'
from Kids
inner join Toys on Kids.id = Toys.kidId
where datediff(year, birthDate, getdate()) <= 5
and count(Toys.Id) > 1
But I am getting the error:
An aggregate may not appear in the WHERE clause unless it is in a subquery contained in a HAVING clause or a select list, and the column being aggregated is an outer reference.
A list with the expected results.
select concat(Kids.firstName,' ',Kids.lastName) as FullName, birthDate
, count(Toys.Id) as 'Number of Toys'
from Kids
inner join Toys on Kids.id = Toys.kidId
where datediff(year, birthDate, getdate()) <= 5
group By Kids.firstName,Kids.lastName,birthDate
having count(Toys.Id) > 1
Your error message says it clearly you can't put an aggregation function in a where clause only in a having.
Still you need to GROUP BY so that every kid appears with his correct count, else you get only q row back
select concat(Kids.firstName,' ',Kids.lastName) as FullName,
birthDate,
count(Toys.Id) as 'Number of Toys'
from Kids
inner join Toys on Kids.id = Toys.kidId
where datediff(year, birthDate, getdate()) <= 5
group by concat(Kids.firstName,' ',Kids.lastName), birthDate
having count(Toys.Id) > 1
FullName
birthDate
Number of Toys
fiddle
Related
I have a problem with using SQL in a database.
This is my code:
CREATE TABLE Supplier
(
Supplier_ID int NOT NULL PRIMARY KEY,
Name varchar(50) NOT NULL,
Phone int,
Email varchar(50)
);
CREATE TABLE Batchofmeat
(
Number_of_batch varchar(50) NOT NULL PRIMARY KEY,
Supplier_ID int NOT NULL REFERENCES Supplier(Supplier_ID),
Weight FLOAT NOT NULL,
Temperature FLOAT NOT NULL,
Price FLOAT NOT NULL
);
SELECT Email
FROM Supplier
WHERE (SELECT Temperature FROM Batchofmeat
WHERE Supplier.Supplier_ID = Batchofmeat.Supplier_ID) < 3;
The problem is that when I run the SELECT, I get the following error:
The subquery has returned more than one value, which is not correct when following =, !=, <, <=, >, >= or when used as an expression
Even if I get the error it should show the solution, what could be the problem?
What I want with my select is to select the email address of those suppliers whose Batchofmeat have a temperature below 3°C.
If you are trying to find all records with a temperature less than 3 you can use the following.
SELECT Email
FROM Supplier
WHERE Supplier_ID IN (SELECT Temperature FROM Batchofmeat WHERE Supplier.Supplier_ID = Batchofmeat.Supplier_ID AND Temperature < 3 );
Note, that this may not be performant with a large number of records.
Since you are adding conditions such as: temperature <3 in your query it is possible that there could be multiple values that meet this criteria. Hence you will have to use the in operator here.
in acts like a list of values.
SELECT Email FROM Supplier
WHERE Supplier_ID IN (SELECT Temperature FROM Batchofmeat
WHERE Supplier.Supplier_ID = Batchofmeat.Supplier_ID AND Temperature < 3 );
It sounds like exists is all you require here:
select email
from supplier s
where exists (select * from BatchOfMeat b where b.temperature < 3 and b.Supplier_Id=s.Supplier_Id )
If could use avg, max, or min temperature if that meets your business requirements.
SELECT Email
FROM Supplier
WHERE
(
SELECT avg(Temperature)
FROM Batchofmeat
WHERE Supplier.Supplier_ID = Batchofmeat.Supplier_ID
) < 3;
Having said that, I would write the SQL query like this:
SELECT Supplier_ID, Email, avg(Temperature)
FROM Supplier inner join Batchofmeat using (Supplier_ID)
group by Supplier_ID, Email
having avg(Temperature) < 3;
UPDATE:
Per your comment: What I want with my select is to select the email address of those suppliers whose Batchofmeat have a temperature below 3°C. –
Do this:
SELECT distinct Supplier_ID, Email
FROM Supplier inner join Batchofmeat using (Supplier_ID)
where Temperature < 3;
Im have the following two tables created:
create table partei(
id int not null primary key ,
name varchar(20),
vorsitzender varchar(20)
);
create table abgeordneter(
name varchar(20),
partei int references partei ,
wahlkreis varchar(20)
)
How can I change this Select-Statement:
SELECT a.Partei
FROM Abgeordneter a, Partei p
WHERE a.Partei = p.ID
GROUP BY a.Partei
HAVING COUNT(a.Name) < 5
Into a statement which doesn't use the having clause, but delivers exactly the same results? Is it even possible?
You can use a subquery an eliminate the JOIN:
SELECT Partei
FROM (SELECT a.Partei, COUNT(*) as cnt
FROM Abgeordneter ap
GROUP BY a.Partei
) a
WHERE cnt < 5;
I have two tables:
COUNTRY, with the two columns COUNTRY_ID and PERSON_REGION_ID.
PERSON, with many columns, in which PERSON_REGION_ID column is same as COUNTRY.PERSON_REGION_ID, and PERSON_ID is ID column of PERSON.
Query is as follows:
SELECT *
from COUNTRY
where PERSON_REGION_ID IN (
SELECT PERSON_REGION_ID
FROM PERSON
WHERE PERSON_ID IN (111, 888)))
AND COUNTRY_ID = 44;
The above query gives results if any one of the ID as matches (111 or 888).
I want the query to give results if both 111 and 888 has matches else return no results.
How this can be achieved?
I would prefer using joins here
EDIT: To answer your comment, it depends if it's procedure or just query. But you declare variables and go with that
By the way, just for the record ... this is T-SQL, not oracle's syntax
declare #CountryID int -- = 44? (if for some reason you keep CountryID as type other
-- then int, just change it to correct one)
declare #Person1 int -- = 111?
declare #Person2 int -- = 888?
select C.* from Country C
join Person P1 on C.Person_Region_ID = P1.PersionRegion_ID and P1.Country_ID = #CountryID
join Person P2 on C.Person_Region_ID = P2.PersionRegion_ID and P2.Country_ID = #CountryID
where P1.PersionID = #Person1 and P2.PersionID = #Person2
One option is to use group by with having in the subquery:
select *
from COUNTRY
where PERSON_REGION_ID IN (
select PERSON_REGION_ID
from PERSON
where PERSON_ID IN ( 111, 888))
group by PERSON_REGION_ID
having count(PERSON_ID) = 2)
and COUNTRY_ID= 44;
If there are duplicate person_id's per PERSON_REGION_ID , you'll need to use distinct with the count.
You can use EXISTS() like this:
SELECT * from COUNTRY t
WHERE EXISTS(SELECT 1 FROM PERSON s
WHERE t.PERSON_REGION_ID = s.PERSON_REGION_ID
AND PERSON_ID IN ( 111, 888)
GROUP BY PERSON_REGION_ID
HAVING COUNT(DISTINCT PERSON_ID) = 2)
AND COUNTRY_ID = 44
I have some troubles with making queries for this task. anyone please help me.
Cars(License, Brand, Year);
Employees(EID, Firstname, Lastname, Wage);
Customers(CID, Firstname, Lastname, Phone);
OfficeStaff(EID, OfficeNumber);
Own(CID, License);
Mechanic(EID, HourlyPrice);
Repairs(License, EID, PartCost, Hours);
Create SQL statements performing the following tasks:
(a) Create the OfficeStaff-table while taking into account that the OfficeNumber may not be NULL and must be in
the range [1..10].
(b) Find the name(s), i.e. firstname(s) and lastname(s), of the owner(s) of the car(s), which have been repaired for
the most times.
(c) Find the average number of hours spent (i.e. Repairs.Hours) repairing cars of brand “Opel”.
(d) Update the HourlyPrice to be 20 EUR for all mechanics with a wage of 100 EUR or more.
My tries:
(a) Create the OfficeStaff-table while taking into account that the OfficeNumber may not be NULL and must be in the range [1..10].
CREATE TABLE OfficeStaff (
EID INT PRIMARY KEY,
Firstname TEXT,
Lastname TEXT,
Wage REAL,
OfficeNumber INT NOT NULL,
CONSTRAINT CK_OfficeNumber CHECK (OfficeNumber BETWEEN 1 AND 10)
b) no idea?!
(c) Find the average number of hours spent (i.e. Repairs.Hours) repairing cars of brand “Opel”.
SELECT AVG(R.Hours)
FROM Repairs R, Cars C
WHERE R.License = C.License AND C.Brand = “Opel”
(d) Update the HourlyPrice to be 20 EUR for all mechanics with a wage of 100 EUR or more.
UPDATE Mechanic
SET HourlyPrice = 20
WHERE Wage >= 100
(a) how to create can look here
CREATE TABLE OfficeStaff (
EID INT PRIMARY KEY,
Firstname varchar(100),
Lastname varchar(100),
Wage decimal(15,2),
OfficeNumber INT NOT NULL,
CONSTRAINT CK_OfficeNumber CHECK (OfficeNumber BETWEEN 1 AND 10)
)
(b) Not sure about this one, but you have to use rank to get not only 1 value among same values. For this you can look here
WITH cte AS (
SELECT a.Firstname, a.Lastname, rank() OVER (ORDER BY count(c.Hours)) AS rnk
from Customers as a
join Own as b
on a.CID=b.CID
join Repairs as c
on b.License = c.License
group by a.Firstname, a.Lastname
)
SELECT *
FROM cte
WHERE rnk <= 1;
(c) join usage here
SELECT AVG(R.Hours)
FROM Repairs R
join Cars C
on R.license=C.license
WHERE C.Brand = 'Opel'
(d) update usage here and here
UPDATE Mechanic
SET HourlyPrice = 20
from Employees
WHERE Mechanic.EID = Employees.EID
AND Employees.Wage >= 100
We returned a list of cardID's after a query and those cardID's belong to two tables Student and Personnel. So how can I join those cardID's with Student and Personnel so I can return a table that shows name of Student and Personnel according to cardID's?
Personnel table:
PERSONNELID NUMBER(9,0)
PERSONNELNAME VARCHAR2(20)
PERSONNELSURNAME VARCHAR2(20)
PERSONNELJOB VARCHAR2(40)
PERSONNELCARDID NUMBER(4,0)
Student table:
STUDENTID NUMBER(9,0)
STUDENTNAME VARCHAR2(20)
STUDENTSURNAME VARCHAR2(20)
STUDENTDEPT VARCHAR2(40)
STUDENTFACULTY VARCHAR2(20)
STUDENTCARDID NUMBER(4,0)
CardID table
CARDID NUMBER(4,0)
USERTYPE VARCHAR2(20)
CHARGE NUMBER(3,2)
CREDIT NUMBER(4,2)
PaymentDevice table:
ORDERNO NUMBER
PAYDEVIP NUMBER(8,0)
PAYDEVDATE DATE No
PAYDEVTIME VARCHAR2(8)
CHARGEDCARDID NUMBER(9,0)
MEALTYPE VARCHAR2(10)
I tried to return first 10 person's name and surname that eat at cafeteria on 27/12/2012
SELECT C.CARDID
FROM CARD C, PAYMENTDEVICE P
WHERE P.ORDERNO
BETWEEN (SELECT MIN(ORDERNO)
FROM PAYMENTDEVICE
WHERE PAYDEVDATE='27/12/2012') AND (SELECT MIN(ORDERNO)
FROM PAYMENTDEVICE
WHERE PAYDEVDATE='27/12/2012')+10 AND C.CARDID=P.CHARGEDCARDID;
Our orderNo isn't reset everyday but keeps increasing so we found the min orderNo that day and add 10 to this value to find first 10 person who eat on that day between those order numbers.
So what return from this query:
CARDID
1005
1000
1002
1003
1009
2000
2001
1007
2002
1004
1006
and those some of those cardId (start with 1) are studentCardId and some of them (starts with 2) are PersonnelCardId. So how can I match and write names accordingly?
SELECT *
FROM Personel p INNER JOIN Student s
ON p.PersonnelCardId = s.StudentCardId
INNER JOIN ReturnedQuery rq
ON rq.CardId = p.PersonnelCardId
updated:
SELECT p.PersonnelName, rq.CardId
FROM Personel p INNER JOIN ReturnedQuery rq
ON rq.CardId = p.PersonnelCardId
UNION
SELECT s.StudentName, rq.Cardid
FROM Student s INNER JOIN ReturnedQuery rq
ON s.StudentCardId = rq.Cardid
Your original query is actually pretty fragile. I'd rewrite it like so (and added the needed joins):
WITH First_Daily_Purchase as (SELECT chargedCardId,
MIN(payDevTime) as payDevTime,
MIN(orderNo) as orderNo
FROM PaymentDevice
WHERE payDevDate >=
TO_DATE('2012-12-27', 'YYYY-MM-DD')
AND payDevDate <
TO_DATE('2012-12-28', 'YYYY-MM-DD')
GROUP BY chargedCardId),
First_10_Daily_Purchasers as (SELECT chargedCardId
FROM (SELECT chargedCardId,
RANK() OVER(ORDER BY payDevTime,
orderNo) as rank
FROM First_Daily_Purchase) a
WHERE a.rank < 11)
SELECT a.chargedCardId, b.personnelName, b.personnelSurname
FROM First_10_Daily_Purchasers a
JOIN Personnel b
ON b.personnelCardId = a.chargedCardId
UNION ALL
SELECT a.chargedCardId, b.studentName, b.studentSurname
FROM First_10_Daily_Purchasers a
JOIN Student b
ON b.studentCardId = a.chargedCardId
(Have a working SQL Fiddle - generally bullet-proofing this took me a while.)
This should get you the first 10 people who made a purchase (not the first 11 purchases, which is what you were actually getting). This of course assumes that payDevTime is actually stored in a sortable format (if it isn't you have bigger problems than this query not working quite right).
That said, there's a number of troubling things about your schema design.