Sequel to get select EVERY input - mysql

I have created three tables
CREATE TABLE guest(
name varchar(100),
ranking int,
PRIMARY KEY (name)
);
CREATE TABLE room(
roomname varchar(100),
wallcolor varchar(100),
rating int,
PRIMARY KEY(roomnane)
);
CREATE TABLE reservation(
name varchar(100),
roomname varchar(100),
day varchar(100),
moveinday int,
moveoutday int,
PRIMARY KEY(roomname, day, start, finish),
FOREIGN KEY(roomname) REFERENCES room(roomname),
FOREIGN KEY(name) REFERENCES guest(name)
);
I am trying to write a query to find the guests who reserved EVERY "LakeView" room
I tried the following
SELECT g.name
FROM guest g, reservation r, room rr
WHERE rr.name = g.name
AND rr.roomname = "LakeView"
GROUP BY g.name
This does not seem to list out every room. How could I gix this?
Input:
insert into guest values ('Andrew', 1);
insert into guest values ('Jack', 4);
insert into guest values ('Jake', 4);
insert into room values ('LakeView', 'white', 10);
insert into room values ('BayView', 'blue', 4);
insert into reservation values ('Andrew', 'LakeView', 'Friday', 10,15);
insert into reservation values ('Jake', 'LakeView', 'Monday', 10,16);
insert into reservation values ('Jack', ' BayView', 'Tuesday', 11,15);
Desired output is
Andrew
Jake

try this:
Solution 1:
SELECT
guest.name
FROM guest INNER JOIN
reservation ON reservation.name = guest.name
INNER JOIN room ON reservation.roomname = room.roomname
WHERE reservation.roomname = "LakeView"
GROUP BY guest.name
Here I've highlighted your mistakes:
You tried to match rr.name with g.name. That means you are
matching roomname with guest name which you didn't want (so far I
guess).
Another mistake is you are only checking "LakeView" only in room
table entries. So other guests who haven't reserved LakeView room
might appear in the final result set. You also need to filter those
records from reservation table which have LakeView roomname.
Here's your query modified based on the above observations:
Solution 2:
SELECT g.name
FROM guest g, reservation r, room rr
WHERE r.name = g.name
AND rr.roomname = "LakeView"
AND r.roomname = "LakeView"
GROUP BY g.name;
N:B: Solution 1 is encouraged to use. Try to avoid implicit joins which are stated in solution 2.
I personally think the INNER JOIN is better, because it is more
readable. It shows better the relations between the table. You got
those relations in the join, and you do the filtering in the WHERE
clause. This separation makes the query more readable.
Have a look at this post.

if you using php-mysql then it will be help
$qry=mysql_query("select name from reservation where roomname='LakeView'");
while($result=mysql_fetch_array($qry)
{
echo $result['name'];
}

I'm assuming that the 'name' column in the reservation table is the name of the guest or guests. If this is true it sounds like you only need the reservation table for the purposes of this query, but if you wanted to combine them all anyway:
select
rv.name as Guest
from room as r
left join reservation as rv on rv.roomname = r.roomname
left join guest as g on g.name = rv.name
where
rv.roomname = 'LakeView'

Related

How do I join on one column being a prefix of another?

Given two tables containing path columns that indicate the hierarchical placement in an organization, and emp1 that is a member of team1 in department1 in company acme (an emp can have multiple paths), and department managers (one manager can manage several departments):
create table emp (
username varchar(10),
path varchar(255)
);
create table manages (
username varchar(10),
path varchar(255)
);
insert into emp (username, path)
values ('emp1', '/acme/dept1/team1'),
('emp1', '/acme/dept9'),
('emp2', '/acme/dept2/team1');
insert into manages (username, path)
values ('mngr1', '/acme/dept1'),
('mngr2', '/acme/dept2'),
('mngr2', '/acme/dept3');
How can I check if emp1 is a subordinate of mngr1, i.e. if there is a manages.path that is a prefix of emp.path?
Something like:
select manages.username
from emp
join manages on manages.path is-prefix-of emp.path
where emp.username = 'emp1' and manages.username = 'mngr1'
which should return mngr1.
The actual path implementation is based on a fixed length primary key encoding (meaning that by construction /acme/dept1 wouldn't be a prefix of /acme/dept10).
Use a LIKE expression here:
SELECT m.username
FROM emp e
INNER JOIN manages m
ON e.path LIKE CONCAT(m.path, '%')
WHERE e.username = 'emp1' AND m.username = 'mngr1';
The LIKE expression in the above query says that e.path starts with m.path.

Associated Name spiderweb

Say for instance I have the following entries in my table:
ID - 1
Name - Daryl
ID - 2
Name - Terry
ID - 3
Name - Dave
ID - 4
Name - Mitch
I eventually wish to search my table(s) for one specific name, but show all associated names. For instance,
Searching Daryl will return Terry, Dave & Daryl.
Searching Terry will return Dave, Daryl & Terry
Searching Mitch will only return Mitch.
The current table housing the names is as followed:
--
-- Table structure for table `members`
--
CREATE TABLE `members` (
`ID` int(255) NOT NULL,
`GuildID` int(255) NOT NULL,
`ToonName` varchar(255) NOT NULL,
`AddedOn` date NOT NULL,
`AddedByID` int(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `members`
--
INSERT INTO `members` (`ID`, `GuildID`, `ToonName`, `AddedOn`, `AddedByID`) VALUES
(1, 1, 'Daryl', '2020-01-17', 5),
(2, 1, 'Terry', '2020-01-17', 5),
(3, 1, 'Mitch', '2020-01-17', 5),
(4, 1, 'Dave', '2020-01-17', 5);
--
For Reference. GuildID will be a default search criteria based on the searchers login details. With a spiderweb like this, how would I go about creating another table (or another Column) to bring a combined search spiderweb structure based on the search criteria?
I was thinking something along the lines of:
CREATE TABLE `Associated`(
`ID` INT(255) NOT NULL,
`MainID` INT(255) NOT NULL,
`SecondaryID` INT(255) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `Associated` (`ID`, `MainID`, `SecondaryID`) VALUES
(1, 1, 2) -- Daryl Associated With Terry
(2, 1, 4) -- Daryl Associated With Dave
But I feel this will make an over complicated value structure with alot of redundant inputs. Is there a more effective way to create a unified search?
The whole idea of operation is that each name is Individual. So certain Entries can be put under Daryl, Terry acting alone. But one search will bring together all associated Names by searching one name then pull together total entries based on the alisas?
You can try This
Select IFNULL(m.ToonName , members.ToonName) as ToonName
from members
LEFT JOIN Associated on Associated.MainID = members.ID
LEFT JOIN members as m on m.ID = Associated.SecondaryID
Where members.ToonName = "Mitch"
While you have entry for
"Mitch" in Associated table it will return you Daryl and when you dont have associated Id it will return the name from members table.
And If you will check this with "Daryl", it will give you Two results,
Select IFNULL(m.ToonName , members.ToonName) as ToonName
from members
LEFT JOIN Associated on Associated.MainID = members.ID
LEFT JOIN members as m on m.ID = Associated.SecondaryID
Where members.ToonName = "Daryl"
In case you want all the names in a single column you can use GROUP_CONCAT as #flash suggested in another answer.
You can directly get the data from the following SQL statement.
For Individual row
SELECT `members`.ToonName FROM `associated` JOIN `members` ON associated.SecondaryID = members.ID WHERE `associated`.MainID = (SELECT ID FROM `members` WHERE ToonName = 'Daryl');
# Output: **ToonName**
Terry,Dave
Grouping Row
// You can also group all rows by comma from following statement
SELECT GROUP_CONCAT(`members`.ToonName) FROM `associated` JOIN `members` ON associated.SecondaryID = members.ID WHERE `associated`.MainID = (SELECT ID FROM `members` WHERE ToonName = 'Daryl');
# Output: **ToonName**
Terry
Dave
Plan A
Add a column to each member. It is the number (or name) if the one group he/she belongs to. Terry, Dave & Daryl would get one value; Mitch would get a different value. Index the column for efficient lookup of related names.
Plan B
Implement a graph, like you suggested. Some tips: Get rid of id; instead have PRIMARY KEY(MainID, SecondaryID). The is an issue to resolve... This design implies a "directedness" of the relationships: Terry --> Dave, but not necessarily Dave --> Terry. If you want to force it to be reflexive, the force two rows to be inserted or insert the two IDs in an canonical order, but then check both directions.
Also, you need to "walk the graph". This is best done with a Recursive CTE. For that feature, you need MySQL 8.0 or MariaDB 10.2.
Plan C
Without the directedness of B, you run into more difficult issues. One is "cluster analysis". Another is messy paths and loops in the 'graph'. Let's avoid these.
Short Answer: Yes you need a second table Associated and it will not make completed structure.
Below is the query to get the required result
SELECT ID, ToonName,
(
SELECT GROUP_CONCAT(ToonName) FROM Associated
JOIN members child ON SecondaryID = child.ID
WHERE MainID = parent.ID
)
FROM members parent
You can also use join but I think, in this case sub query will be better.
NOTE : your tables need some optimization like remove ID field from Associated table, Add index etc.

SQL - Column in field list is ambiguous

I have two tables BOOKINGS and WORKER. Basically there is table for a worker and a table to keep track of what the worker has to do in a time frame aka booking. I’m trying to check if there is an available worker for a job, so I query the booking to check if requested time has available workers between the start end date. However, I get stuck on the next part. Which is returning the list of workers that do have that time available. I read that I could join the table passed on a shared column, so I tried doing an inner join with the WORKER_NAME column, but when I try to do this I get a ambiguous error. This leads me to believe I misunderstood the concept. Does anyone understand what I;m trying to do and knows how to do it, or knows why I have the error below. Thanks guys !!!!
CREATE TABLE WORKER (
ID INT NOT NULL AUTO_INCREMENT,
WORKER_NAME varchar(80) NOT NULL,
WORKER_CODE INT,
WORKER_WAGE INT,
PRIMARY KEY (ID)
)
CREATE TABLE BOOKING (
ID INT NOT NULL AUTO_INCREMENT,
WORKER_NAME varchar(80) NOT NULL,
START DATE NOT NULL,
END DATE NOT NULL,
PRIMARY KEY (ID)
)
query
SELECT *
FROM WORKERS
INNER JOIN BOOKING
ON WORKER_NAME = WORKER_NAME
WHERE (START NOT BETWEEN '2010-10-01' AND '2010-10-10')
ORDER BY ID
#1052 - Column 'WORKER_NAME' in on clause is ambiguous
In your query, the column "worker_name" exists in two tables; in this case, you must reference the tablename as part of the column identifer.
SELECT *
FROM WORKERS
INNER JOIN BOOKING
ON workers.WORKER_NAME = booking.WORKER_NAME
WHERE (START NOT BETWEEN '2010-10-01' AND '2010-10-10')
ORDER BY ID
In your query, the column WORKER_NAME and ID columns exists in both tables, where WORKER_NAME retains the same meaning and ID is re-purposed; in this case, you must either specify you are using WORKER_NAME as the join search condition or 'project away' (rename or omit) the duplicate ID problem.
Because the ID columns are AUTO_INCREMENT, I assume (hope!) they have no business meaning. Therefore, they could both be omitted, allowing a natural join that will cause duplicate columns to be 'projected away'. This is one of those situations where one wishes SQL had a WORKER ( ALL BUT ( ID ) ) type syntax; instead, one is required to do it longhand. It might be easier in the long run to to opt for a consistent naming convention and rename the columns to WORKER_ID and BOOKING_ID respectively.
You would also need to identify a business key to order on e.g. ( START, WORKER_NAME ):
SELECT *
FROM
( SELECT WORKER_NAME, WORKER_CODE, WORKER_WAGE FROM WORKER ) AS W
NATURAL JOIN
( SELECT WORKER_NAME, START, END FROM BOOKING ) AS B
WHERE ( START NOT BETWEEN '2010-10-01' AND '2010-10-10' )
ORDER BY START, WORKER_NAME;
This is good, but its returning the start and end times as well. I'm just wanting the WOKER ROWS. I cant take the start and end out, because then sql doesn’t recognize the where clause.
Two approaches spring to mind: push the where clause to the subquery:
SELECT *
FROM
( SELECT WORKER_NAME, WORKER_CODE, WORKER_WAGE FROM WORKER ) AS W
NATURAL JOIN
( SELECT WORKER_NAME, START, END
FROM BOOKING
WHERE START NOT BETWEEN '2010-10-01' AND '2010-10-10' ) AS B
ORDER BY START, WORKER_NAME;
Alternatively, replace SELECT * with a list of columns you want to SELECT:
SELECT WORKER_NAME, WORKER_CODE, WORKER_WAGE
FROM
( SELECT WORKER_NAME, WORKER_CODE, WORKER_WAGE FROM WORKER ) AS W
NATURAL JOIN
( SELECT WORKER_NAME, START, END FROM BOOKING ) AS B
WHERE START NOT BETWEEN '2010-10-01' AND '2010-10-10'
ORDER BY START, WORKER_NAME;
This error comes after you attempt to call a field which exists in both tables, therefore you should make a reference. For instance in example below I first say cod.coordinator so that DBMS know which coordinator I want
SELECT project__number, surname, firstname,cod.coordinator FROMcoordinatorsAS co JOIN hub_applicants AS ap ON co.project__number = ap.project_id JOIN coordinator_duties AS cod ON co.coordinator = cod.email

mysql query output projector and whiteboard

I've got this small assignment:
Find those rooms that have projectors, but not whiteboards.
CREATE TABLE Equipment
(
room VARCHAR(15),
type VARCHAR(20)
);
INSERT INTO Equipment VALUES ('Dreyer-201','projector');
INSERT INTO Equipment VALUES **('Zuse-127','projector');**
INSERT INTO Equipment VALUES ('Shannon-164','projector');
INSERT INTO Equipment VALUES ('Dreyer-201','whiteboard');
INSERT INTO Equipment VALUES **('Zuse-127','whiteboard');**
INSERT INTO Equipment VALUES ('Shannon-164','whiteboard');
This is my answer:
SELECT DISTINCT room, type
FROM Equipment
WHERE type = 'projector' AND NOT type = 'whiteboard'
And it is working - but my output lists the room Zuse-127. I know it stands alone with both projector and whiteboard, but how can I fix this problem? So the room Zuse-127 does not show.
Try this one use NOT IN() for the rooms which belongs to whiteboard
SELECT DISTINCT room, type
FROM Equipment
WHERE type = 'projector' AND room
NOT IN (SELECT `room` FROM Equipment WHERE type = 'whiteboard' )
A self-join is likely to perform much better than solutions using GROUP BY or subqueries.
SELECT e.room, e.type
FROM Equipment AS e
LEFT OUTER JOIN Equipment AS e2
ON e.room = e2.room AND e2.type = 'whiteboard'
WHERE e.type = 'projector'
AND e2.type IS NULL;
Put an index on (type,room) for best results.

field in subquery based on age of row instead of "group by"

I can't seem to get this query right. I have tables like this (simplified):
person: PersonID, ...other stuff...
contact: ContactID, PersonID, ContactDate, ContactTypeID, Description
I want to get a list of all the people who had a contact of a certain type (or types) but none of another type(s) that occurred later. An easy-to-understand example: Checking for records of gifts received without having sent a thank-you card afterward. There might have been other previous thank-you cards sent (pertaining to other gifts), but if the most recent occurrence of a Gift Received (we'll say that's ContactTypeID=12) was not followed by a Thank You Sent (ContactTypeID=11), the PersonID should be in the result set. Another example: A mailing list would be made up of everyone who has opted in (12) without having opted out (11) more recently.
My attempt at a query is this:
SELECT person.PersonID FROM person
INNER JOIN (SELECT PersonID,ContactTypeID,MAX(ContactDate) FROM contact
WHERE ContactTypeID IN (12,11) GROUP BY PersonID) AS seq
ON person.PersonID=seq.PersonID
WHERE seq.ContactTypeID IN (12)`
It seems that the ContactTypeID returned in the subquery is for the last record entered in the table, regardless of which record has the max date. But I can't figure out how to fix it. Sorry if this has been asked before (almost everything has!), but I don't know what terms to search for.
Wow. A system to check who has been good and sent thank yous. I think I would be in your list...
Anyway. Give this a go. The idea is to create two views: the first with personId and the time of the most recently received gift and the second with personId and the most recently sent thanks. Join them together using a left outer join to ensure that people who have never sent a thank you are included and then add in a comparison between the most recently received time and the most recent thanks time to find impolite people:
select g.personId,
g.mostRecentGiftReceivedTime,
t.mostRecentThankYouTime
from
(
select p.personId,
max(ContactDate) as mostRecentGiftReceivedTime
from person p inner join contact c on p.personId = c.personId
where c.ContactTypeId = 12
group by p.personId
) g
left outer join
(
select p.personId,
max(ContactDate) as mostRecentThankYouTime
from person p inner join contact c on p.personId = c.personId
where c.ContactTypeId = 11
group by p.personId
) t on g.personId = t.personId
where t.mostRecentThankYouTime is null
or t.mostRecentThankYouTime < g.mostRecentGiftReceivedTime;
Here is the test data I used:
create table person (PersonID int unsigned not null primary key);
create table contact (
ContactID int unsigned not null primary key,
PersonID int unsigned not null,
ContactDate datetime not null,
ContactTypeId int unsigned not null,
Description varchar(50) default null
);
insert into person values (1);
insert into person values (2);
insert into person values (3);
insert into person values (4);
insert into contact values (1,1,'2013-05-01',12,'Person 1 Got a present');
insert into contact values (2,1,'2013-05-03',11,'Person 1 said "Thanks"');
insert into contact values (3,1,'2013-05-05',12,'Person 1 got another present. Lucky person 1.');
insert into contact values (4,2,'2013-05-01',11,'Person 2 said "Thanks". Not sure what for.');
insert into contact values (5,2,'2013-05-08',12,'Person 2 got a present.');
insert into contact values (6,3,'2013-04-25',12,'Person 3 Got a present');
insert into contact values (7,3,'2013-04-30',11,'Person 3 said "Thanks"');
insert into contact values (8,3,'2013-05-02',12,'Person 3 got another present. Lucky person 3.');
insert into contact values (9,3,'2013-05-05',11,'Person 3 said "Thanks" again.');
insert into contact values (10,4,'2013-04-30',12,'Person 4 got his first present');