Basically i have these relations, account(account_number,branch_name,balance) and city(branch_name,country). What i want is all the account_numbers of the people who have accounts on all the cities in the city table but for some reason im getting an empty query and i dont get why.
Ps: ive seen some confusion on what the divide means, the divide is the division of 2 sets like in relational algebra, what i want is to divide the account set with the city set, in doing so, it is supposed to return all accounts that have a branch_name in every city in cities
Example:
account table
--------------|-----------|---------
account_number|branch_name|balance
--------------|-----------|---------
'A-000000' |'Downtown' | 3467
'A-000001' |'Downtown' | 1500
'A-000002' |'London' | 1500
'A-000826' |'Manchester'| 9999999
'A-000826' |'Downtown' | 33399
------------------------------------
city
--------------|-----------
branch_name | country
--------------|-----------
'Manchester' | 'UK'
'Downtown' | 'USA'
--------------------------
Account รท city
--------------|-----------|---------
account_number|branch_name|balance
--------------|-----------|---------
'A-000826' |'Manchester'| 9999999
'A-000826' |'Downtown' | 33399
-----------------------------------
'A-000826' is the only account in all of the cities in the city table
Program:
create table account(
account_number char(9),
branch_name varchar(80) not null,
balance numeric(16,4));
create table city(
branch_name varchar(80) not null,
country varchar(80) not null,
check(branch_name != ''));
INSERT INTO city(branch_name,country)
VALUES ('Manchester','UK'),
('Downtown','USA');
INSERT INTO account(account_number, branch_name, balance)
VALUES ('A-000000','Downtown',3467),
('A-000001','Downtown',1500),
('A-000002','London',1500),
('A-000826','Manchester',9999999),
('A-000826','Downtown',33399);
Query:
select *
from account as A
where not exists ( (select branch_name
from account)
except
(select C.branch_name
from city as C
where A.branch_name = C.branch_name)
)
I agree with Tim Biegeleisen's answer.
Your logic in the where not exists was backwards.
If you insist on sticking to relational algebra, then try this, instead:
select *
from account as A
where not exists (
select branch_name
from city
except
select branch_name
from account b
where b.account_number = a.account_number
);
db<>fiddle here
I would use an aggregation approach:
SELECT a.account_number
FROM account a
INNER JOIN city c
ON c.branch_name = a.branch_name
GROUP BY a.account_number
HAVING COUNT(*) = (SELECT COUNT(*) FROM city);
Here are my tables:
create table country(
country_id number(5) primary key,
country varchar2(36)
);
create table city(
city_id number(5) primary key,
country_id number(5) constraint city_country_fk references country(country_id) NOT NULL,
city varchar2(36)
);
SQL> desc airportdemodata;
Name Null? Type
----------------------------------------- -------- ----------------------------
AIRPORT_ID NOT NULL NUMBER(5)
IATA_CODE VARCHAR2(3)
CITY VARCHAR2(36)
COUNTRY VARCHAR2(36)
AIRPORT VARCHAR2(58)
I inserted countries from airportdemodata to country table as under:
INSERT INTO country (country)
SELECT unique(country) from airportdemodata;
it worked fine.
Now I am trying to copy airportdemodata.city into city.city(country_id,city) by matching airportdemodata.country with country.country. I tried like this:
SQL> SELECT a.unique(city), b.country_id FROM airportdemodata a, country b where a.country=b.country;
SELECT a.unique(city), b.country_id FROM airportdemodata a, country b where a.country=b.country
*
ERROR at line 1:
ORA-01747: invalid user.table.column, table.column, or column specification
Right; inventing your own syntax usually leads to the outcome you experienced. Did you mean to use distinct?
SELECT DISTINCT a.city,
b.country_id
FROM airportdemodata a
JOIN country b ON a.country = b.country;
I am having trouble selecting all values from a table.
I need to select all vehicles where their vRentTimes value is the same as the times they appear in the Renting table.
So basically just checking if the recordings of rentings are correct.
This is the description of tables:
Vehicle
Name Null? Type
------------ -------- ------------
VPLATENUMBER NOT NULL VARCHAR2(7)
VCOLOR NOT NULL VARCHAR2(10)
VCC NOT NULL NUMBER
VHORSEPOWER NOT NULL NUMBER
VRENTTIMES NUMBER
VEHCATNAME NOT NULL VARCHAR2(20)
Renting
Name Null? Type
------------ -------- -----------
CAFM NOT NULL VARCHAR2(9)
VPLATENUMBER NOT NULL VARCHAR2(7)
OUTDATE NOT NULL DATE
INDATE DATE
I see. vRentTimes is supposed to be a count for the second table. You can use JOIN and `aggregation. To get the rows that do not match:
select v.*, r.cnt
from vehicles v left join
(select VPLATENUMBER, count(*) as cnt
from renting r
group by VPLATENUMBER
) r
on r.VPLATENUMBER = v.VPLATENUMBER
where v.VRENTTIMES <> coalesce(cnt, 0);
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.
For a particular name i want to fetch other names who have lived in three or more cities lived by this person.
this is what you should do:
redesign your database to have a city table:
city(id int, name varchar)
and a user table:
user(id int, name varchar, ...)
and a user_city table:
user_city(user_id int, city_id int)
that alone will eliminate the limit of 10 cities per user.
to find the cities lived in by a user:
select city_id form user_city where user_id = ?
now how you would find users that lives in 3 or more cities from that list?
one way to do it would be to count the number of cities from the list each user lived in, something like:
select user_id,count(*) n
from user_city
where city_id in (select city_id
from user_city
where user_id = ?)
group by user_id having n >= 3;
I didn't really test this, but it should work.
you will also have to figure out how to index those tables.
You'd need binomial(10,3)^2 OR conditions to do your query. Thats 14 400. You do not want to do that.
You need to redesign your table instaed of
name , city1 , city2 , city3 ,city4 , city5 ,city6 , city7 , city8 , city9 city10
it should be more like
Person, City, rank
------------------
name , city1 ,1
name , city2 ,2
name , city3 ,3
name , city4 ,4
name , city5 ,5
name , city6 ,6
name , city7 ,7
name , city8 ,8
name , city9 ,9
name , city10,10
and take TomTom's advice and learn about data normalization!
Respecting your request to not redesign the database
My untried idea, no way to test it right now
Make a view (name, city) by unioning select name, c1, select name, c2 etc...
Then:
select m2.name from myview m1
inner join myview m2 on m1.city = m2.city
where m1.name = #Name AND m2.Name!=#Name
group by m2.name
having count(m2.name) > 2
Yeah.
You send the table back to whoever designed it with a comment to learn hwo to design tables. First normal form, normalization.
Once the table follows SQL rules, the query is pretty easy.
Try something like this:
SELECT PersonName,COUNT(*) AS CountOf
FROM (SELECT PersonName,city1 FROM PersonCities WHERE city1 IS NOT NULL
UNION SELECT PersonName,city2 FROM PersonCities WHERE city2 IS NOT NULL
UNION SELECT PersonName,city3 FROM PersonCities WHERE city3 IS NOT NULL
UNION SELECT PersonName,city4 FROM PersonCities WHERE city4 IS NOT NULL
UNION SELECT PersonName,city5 FROM PersonCities WHERE city5 IS NOT NULL
...
) dt
WHERE dt.city1 IN (SELECT city1 FROM PersonCities WHERE PersonName=..SearchPerson.. AND city1 IS NOT NULL
UNION SELECT city2 FROM PersonCities WHERE PersonName=..SearchPerson.. AND city2 IS NOT NULL
UNION SELECT city3 FROM PersonCities WHERE PersonName=..SearchPerson.. AND city3 IS NOT NULL
UNION SELECT city4 FROM PersonCities WHERE PersonName=..SearchPerson.. AND city4 IS NOT NULL
UNION SELECT city5 FROM PersonCities WHERE PersonName=..SearchPerson.. AND city5 IS NOT NULL
...
)
AND PersonName!=#SearchPerson
GROUP BY PersonName
HAVING COUNT(*)>=3
I don't have mysql, so here it is running using SQL Server:
DECLARE #PersonCities table(PersonName varchar(10), city1 varchar(10), city2 varchar(10), city3 varchar(10), city4 varchar(10), city5 varchar(10))
INSERT INTO #PersonCities VALUES ('Joe','AAA','BBB','CCC', NULL, NULL)
INSERT INTO #PersonCities VALUES ('Pat','BBB','DDD','EEE','FFF','GGG')
INSERT INTO #PersonCities VALUES ('Sam','FFF','BBB', NULL, NULL, NULL)
INSERT INTO #PersonCities VALUES ('Ron','HHH','DDD','EEE','FFF', NULL)
INSERT INTO #PersonCities VALUES ('Don','FFF','ZZZ','QQQ', NULL, NULL)
DECLARE #SearchPerson varchar(10)
SET #SearchPerson='Pat'
SELECT PersonName,COUNT(*) AS CountOf
FROM (SELECT PersonName,city1 FROM #PersonCities WHERE city1 IS NOT NULL
UNION SELECT PersonName,city2 FROM #PersonCities WHERE city2 IS NOT NULL
UNION SELECT PersonName,city3 FROM #PersonCities WHERE city3 IS NOT NULL
UNION SELECT PersonName,city4 FROM #PersonCities WHERE city4 IS NOT NULL
UNION SELECT PersonName,city5 FROM #PersonCities WHERE city5 IS NOT NULL
) dt
WHERE dt.city1 IN (SELECT city1 FROM #PersonCities WHERE PersonName=#SearchPerson AND city1 IS NOT NULL
UNION SELECT city2 FROM #PersonCities WHERE PersonName=#SearchPerson AND city2 IS NOT NULL
UNION SELECT city3 FROM #PersonCities WHERE PersonName=#SearchPerson AND city3 IS NOT NULL
UNION SELECT city4 FROM #PersonCities WHERE PersonName=#SearchPerson AND city4 IS NOT NULL
UNION SELECT city5 FROM #PersonCities WHERE PersonName=#SearchPerson AND city5 IS NOT NULL
)
AND PersonName!=#SearchPerson
GROUP BY PersonName
HAVING COUNT(*)>=3
OUTPUT:
PersonName
---------- -----------
Ron 3
(1 row(s) affected)
You need to normalize your database.
Doing that you will get the columns
Name, City (optionally CityOrder).
After that you will need to find a way to combine these results into what you need. Doing this you'll need to understand Join, Count and Group by.
Try this:
< table > Person
< fields > PersonId, PersonName |
< table > City
< fields > CityId, CityName |
< table > LivedIn
< fields > LivedInId, PersonId, CityId
Logically you would do the following things for each scenario:
Find the person who has lived in the maximum number of different cities:
Make a list of the PersonId's (all people)
Iterate over that and count the number of cities each person lived in
Find the maximum cities lived in by anyone person
Find the person name related to the personId that had the max cities
Find all people that lived in 3 or more cities as a give person
Let's call the person Bob
Make a list of all cities (the CityIds) that Bob lived in.
Make a list which includes personId, and common cities (maybe a HashMap in Java)
Iterate over the LivedIn table and update the count of how many cities are common
Find all the people who have a count greater than 3
I would do this with a combination of Java and SQL but I am not that good with either so can't give you the code here without having to look a lot of stuff up.
Breaking this data out into three tables to provide a more flexible many-to-many relationship.
person table to store names
city table to store cities
person_city to relate the two (many to many)
To retrieve other people who have lived in 3 or more cities that navin has:
SELECT name FROM (
SELECT
p.name, COUNT(DISTINCT(city_id)) AS lived
FROM person p
JOIN person_city pc ON (pc.person_id = p.person_id)
JOIN city c ON (c.city_id = pc.city_id)
WHERE city_id IN (
SELECT c2.city_id
FROM city c2
JOIN person_city pc2 ON (c2.city_id = pc2.city_id)
JOIN person p2 ON (p2.person_id = pc2.person_id)
WHERE p2.name = 'navin'
)
GROUP BY person_id HAVING lived >= 3
) AS multihome
WHERE name <> 'navin';