I'm working on a program that allows a user to create events, and have other users RSVP to those events. When fetching these events, I would like to be able to sort the attendees by when they have RSVP'd (both those that have accepted, and those that have declined). I store the time that a user RSVP'd in the database. These are the relevant tables:
CREATE TABLE users (
username VARCHAR(50) NOT NULL,
server_id VARCHAR(40) NOT NULL,
role VARCHAR(10),
PRIMARY KEY (username, server_id),
FOREIGN KEY (server_id) REFERENCES servers(server_id)
ON DELETE CASCADE
);
CREATE TABLE events (
server_id VARCHAR(40) NOT NULL,
start_time DATETIME NOT NULL,
time_zone VARCHAR(5) NOT NULL,
title VARCHAR(256) NOT NULL,
description VARCHAR(1000),
PRIMARY KEY (server_id, title),
FOREIGN KEY (server_id) REFERENCES servers(server_id)
ON DELETE CASCADE
);
CREATE TABLE user_event (
username VARCHAR(50) NOT NULL,
server_id VARCHAR(40) NOT NULL,
title VARCHAR(256) NOT NULL,
attending BOOLEAN NOT NULL,
last_updated DATETIME NOT NULL,
PRIMARY KEY (username, server_id, title),
FOREIGN KEY (server_id, title) REFERENCES events(server_id, title)
ON DELETE CASCADE,
FOREIGN KEY (server_id, username) REFERENCES users(server_id, username)
ON DELETE CASCADE
);
Previous to wanting to sort by when a user RSVP'd, I was using this query and it was working great.
SELECT title, description, start_time, time_zone, (
SELECT GROUP_CONCAT(DISTINCT username)
FROM user_event
WHERE user_event.server_id = %s
AND user_event.title = %s
AND user_event.attending = 1)
AS accepted, (
SELECT GROUP_CONCAT(DISTINCT username)
FROM user_event
WHERE user_event.server_id = %s
AND user_event.title = %s
AND user_event.attending = 0)
AS declined
FROM events
WHERE server_id = %s
AND title = %s;
However, adding an ORDER BY last updated clause to the two nested SELECT statements does not seem to have any effect. Is this a limitation of MySQL, or is there still a way I can accomplish this?
You probably want ORDER BY inside GROUP_CONCAT:
SELECT title, description, start_time, time_zone, (
SELECT GROUP_CONCAT(DISTINCT username ORDER BY last updated)
FROM user_event
WHERE user_event.server_id = %s
AND user_event.title = %s
AND user_event.attending = 1)
AS accepted, (
SELECT GROUP_CONCAT(DISTINCT username ORDER BY last updated)
FROM user_event
WHERE user_event.server_id = %s
AND user_event.title = %s
AND user_event.attending = 0)
AS declined
FROM events
WHERE server_id = %s
AND title = %s;
Related
I have three tables, patient_set, user and map_patient_set_user. I'm trying to do an inner join between patient_set and user and also patient_set and map_patient_set_user in a single query. The query should return all the patient_sets for which a given user is the creator or care_coordinator. But patient sets can also be returned if they have the required permission (specified in map_patient_set_user). The following query ends up returning data from only the map_patient_set_user and ignores the join between patient_set and user. What should be the correct way to implement this join.
//Query
SELECT *
FROM (
patient_set AS a
JOIN user AS b ON a.creator_id = b.user_id OR a.care_coordinator = b.user_id
JOIN map_patient_set_user AS m ON a.patient_set_id = m.patient_set_id
)
WHERE
b.user_id = UUID_TO_BIN_F(:id)
OR
m.permission = 'view_all'
ORDER BY created DESC;
//Tables
CREATE TABLE user (
user_id BINARY(16) PRIMARY KEY NOT NULL,
first_name VARCHAR(55) NOT NULL,
last_name VARCHAR(25) NOT NULL,
org_id INT UNSIGNED,
FOREIGN KEY (org_id) REFERENCES organization(org_id),
lru_patient_set_id BINARY(16)
);
CREATE TABLE patient_set (
patient_set_id BINARY(16) PRIMARY KEY NOT NULL,
creator_id BINARY(16) NOT NULL,
FOREIGN KEY (creator_id) REFERENCES user(user_id),
created DATETIME NOT NULL,
last_updated DATETIME,
care_coordinator BINARY(16),
FOREIGN KEY (care_coordinator) REFERENCES user(user_id),
num_patient_updates INT NOT NULL,
name VARCHAR(100)
);
CREATE TABLE map_patient_set_user (
patient_set_id BINARY(16),
user_id BINARY(16),
permission ENUM('view_no_personal', 'view_all', 'edit_all'),
FOREIGN KEY(patient_set_id) REFERENCES patient_set(patient_set_id),
FOREIGN KEY(user_id) REFERENCES user(user_id)
);
I'm setting up a chat-service.
The table for my messages is created as followed:
CREATE TABLE message (
fromId int NOT NULL,
toId int NOT NULL,
message text NOT NULL,
timestamp timestamp NOT NULL,
toRead int NOT NULL,
FOREIGN KEY (fromId) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (toId) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
As a user I should get all other users I already chatted with. Therefore I create an sql-statement:
SELECT u.name, u.surname, u.id, m.toRead, m.timestamp, m.fromId, m.toId
FROM message m, user_informations u
WHERE (m.toId = '2' AND m.fromId = u.id) OR (m.fromId = '2' AND m.toId = u.id)
GROUP BY concat(m.fromId, m.toId OR m.toId, m.fromId)
ORDER BY m.timestamp LIMIT 10;
It works as long as one users only writes the other. If both have written each other I get two returned columns
'Peter','Tester','1','0','2015-07-27 16:10:11','1','2'
'Peter','Tester','1','0','2015-07-27 17:14:22','2','1'
because fromId in first result is other than in second and same with toId.
How can I get the statement to return only one row?
I have two tables: attendees & history. History table is where I post payments for all the attendees. It also stores an historyid and the attendeeid. The query I'm using to try to get to the last record entered in the history table for a particular attendee is:
$stmt = $db->prepare('SELECT a.fname, a.lname, h.amount, h.subsidy, h.last_payment, h.balance
FROM history AS h
INNER JOIN
attendees AS a
ON a.attendeeid = h.attendeeid
WHERE a.attendeeid = :id
ORDER BY historyid DESC LIMIT 1)');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$result = $stmt->fetchAll();
But the code is failing to return anything at all. Since there will likely be historyid's greater than the one I'm trying to retrieve, how do I associate the greatest historyid for that attendee so I get the last record entered for them in the History table? What I'm using is obviously not the answer. Thanks in advance for your help.
Sorry. The schemas are:
TABLE attendees (
attendeeid int(10) unsigned NOT NULL AUTO_INCREMENT,
fname varchar(20) NOT NULL,
lname varchar(20) NOT NULL,
dojid varchar(10) NOT NULL,
address1 varchar(25) NOT NULL,
address2 varchar(25) NOT NULL,
city varchar(20) NOT NULL,
state char(2) NOT NULL,
zipcode varchar(5) NOT NULL,
phonenumber varchar(15) NOT NULL,
memberid int(10) unsigned NOT NULL,
PRIMARY KEY (attendeeid),
KEY memberid (memberid),
CONSTRAINT attendees_ibfk_2 FOREIGN KEY (memberid) REFERENCES members (memberid) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
TABLE history (
historyid int(10) unsigned NOT NULL AUTO_INCREMENT,
amount float NOT NULL,
subsidy char(1) NOT NULL,
last_payment date NOT NULL,
amount_paid float NOT NULL,
balance float NOT NULL,
attendeeid int(10) unsigned NOT NULL,
memberid int(10) unsigned NOT NULL,
PRIMARY KEY (historyid),
KEY attendeeid (attendeeid),
CONSTRAINT history_ibfk_2 FOREIGN KEY (attendeeid) REFERENCES attendees (attendeeid) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Please can you provide the structure of both tables? I'm guessing your tables contains a date columns, or something like that.
Anyway, I would try something like:
select *
from (select a.attendeeid,max(h.id)
FROM history AS h INNER JOIN attendees AS a ON a.attendeeid = h.attendeeid
group by a.attendeeid) as maxHistoryPerAttendee
In that way, you will have the maximun history id per each attendeeid. I'm guessing the max id, is the last inserted row.
This is your query with a couple fields added:
select *
from (select a.attendeeid,max(h.historyid),fname,lname,last_payment,amount
FROM history AS h INNER JOIN attendees AS a ON a.attendeeid = h.attendeeid
group by a.attendeeid) as maxHistoryPerAttendee
where attendeeid = 29
In doing that, I got the first-row entry for that attendee's last_payment instead of the last_payment associated with the MAX(historyid) row.
To add to this and call it done, I got this to work. It's very near my original query.
$stmt = $db->prepare('SELECT fname, lname, amount, subsidy, last_payment, balance, a.attendeeid, h.historyid
FROM history AS h
INNER JOIN attendees AS a ON a.attendeeid = h.attendeeid
where h.attendeeid = :id
ORDER BY h.historyid DESC LIMIT 1');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
Works like a champ!
I'm guessing this is too local but I can't figure out a way to make it more general (which might be why I'm not able to find my answer on Google).
We have an application that tracks contacts for our business. These contacts (Contact table) are either contacted through the phone (Contact_Phone table) or through email (Contact_Email). If the user is contacted through the phone an agent keeps track of the total number of seconds (Contact_Phone.totalSeconds). Through a piece of business logic that I have no control over email contacts are treated as one second. A user might be contact through just email, just phone, or both.
I'm trying to generate a report on how long we've spent contacting each user but I can't get the results I expect.
Tables:
CREATE TABLE IF NOT EXISTS `Contact` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `Contact_Email` (
`id` INT NOT NULL AUTO_INCREMENT ,
`ContactId` INT NULL ,
PRIMARY KEY (`id`) ,
INDEX `contact_email_contact_idx` (`ContactId` ASC) ,
CONSTRAINT `contact_email_contact`
FOREIGN KEY (`ContactId` )
REFERENCES `Contact` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `Contact_Phone` (
`id` INT NOT NULL AUTO_INCREMENT ,
`totalSeconds` INT NULL ,
`ContactId` INT NULL ,
PRIMARY KEY (`id`) ,
INDEX `Contact_Phone_contact_idx` (`ContactId` ASC) ,
CONSTRAINT `Contact_Phone_contact`
FOREIGN KEY (`ContactId` )
REFERENCES `Contact` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Test Data:
insert into Contact (id, name) values (1, 'Scott');
insert into Contact (id, name) values (2, 'Joe');
insert into Contact_Phone (totalSeconds, ContactId) values (10, 2);
insert into Contact_Phone (totalSeconds, ContactId) values (100, 2);
insert into Contact_Email (ContactId) values (1);
insert into Contact_Email (ContactId) values (1);
insert into Contact_Email (ContactId) values (2);
Query:
select
name,
(select sum(totalSeconds) from Contact_Phone where Contact_Phone.ContactId = Contact.id)
+
(select count(*) from Contact_Email where Contact_Email.ContactId = Contact.id)
from Contact;
Expected Results:
Joe 111
Scott 2
Actual Results:
Joe 111
Scott null
Thanks
How about using summaries and LEFT JOIN operations, like so?
SELECT Contact.name,
COALESCE(p.seconds,0) + COALESCE(e.seconds,0) seconds
FROM Contact.Name
LEFT JOIN (
SELECT ContactID AS id,
SUM(totalSeconds) AS seconds
FROM ContactPhone
GROUP BY ContactID
) p ON Contact.id = p.id
LEFT JOIN (
SELECT ContactID AS id,
COUNT(*) AS seconds
FROM ContactEmail
GROUP BY ContactID
) e ON Contact.id = e.id
The LEFT JOIN operations will preserve your result rows where one or the other of your "seconds" computations is NULL. And, the COALESCE operations will prevent your query from attempting arithmetic on NULL values, which yields NULL.
I have a query against two databases that I'm trying to execute. The first table is just user information and is referenced by a privilege table. For my query I'm trying to find a set of super users, they are users that have every current privilege in the privilege table. It is set up as follows:
create table MEMBER
( id int not null,
name varchar(10),
bdate date,
sex char,
pc_id int not null,
PRIMARY KEY (mid),
FOREIGN KEY (pc_id) REFERENCES PLEDGECLASS(id))
create table MEMBER_PRIVILEGE
( mid int not null,
pid int not null,
PRIMARY KEY (mid,pid),
FOREIGN KEY (mid) REFERENCES MEMBER(id),
FOREIGN KEY (pid) REFERENCES PRIVILEGE(id))
create table PRIVILEGE
( id int,
pname varchar(15)
PRIMARY KEY(id))
Although obviously the incorrect query, I'm trying to do something equivalent to the following:
Select name
From MEMBER,MEMBER_PRIVILEGE
Where id=mid AND pid = ALL (select id
From PRIVILEGE);
SELECT *
FROM MEMBER
WHERE NOT EXISTS (
SELECT *
FROM MEMBER_PRIVILEGE LEFT JOIN PRIVILEGE ON MEMBER_PRIVILEGE.pid = PRIVILEGE.id)
WHERE MEMBER_PRIVILEGE.mid = MEMBER.id AND PRIVILEGE.id IS NULL
)
Try this ::
Select name, (select count(id) from PRIVILIGE) as p_count
From MEMBER m
inner join MEMBER_PRIVILEGE mp on (m.id=mp.mid)
group by mp.mid having count(*) = p_count;