Suitable MySQL query for latest status - php

I work for a hosting company were we often get requests for installs, new domains, lag fixes etc. To get a bit of an overview of what is still open I decided to make a really simple ticket system. I have a bit of php knowledge and a bit of MySQL knowledge. For now we will be submitting the tickets ourselves based on e-mails and phonecalls from customers.
I have created the following SQL structure:
|| TICKETS || || STATUSSES || || STATUS-CODES ||
-------------- ---------------- -------------------
| ID | | ticket_id | | ID |
| cust_name | | status_id | | status_txt |
| cust_cntct | | datetime | -------------------
| subject | ----------------
| message |
--------------
I now want to make the overviw table showing all the existing tickets. A ticket is inserted with a default status. The concurring timestamp will be the time that the ticket was added. Every time a ticket moves to the next status a new status will be added with a timestamp. The newest status is always the current status.
I can't figure out how to create a query that will get every ticket with its latest status. A simple join will return a ticket as many times as the amount of statusses it has. I want to create a join but only show the results were the timestamp of the status is the newest for a certain ticket.

Try this:
SELECT a.*, b.`datettime`, d.status_txt
FROM tickets a
INNER JOIN statusses b
on a.id = b.ticket_ID
INNER JOIN
(
SELECT ticket_ID, MAX(`datetime`) CurrentTicketDate
FROM Statuses
GROUP BY ticket_ID
) c
ON b.ticket_ID = c.tecket_ID AND
b.`datetime` = c.CurrentTicketDate
INNER JOIN Status_Codes d
on b.status_ID = d.id

Related

Connect two tables and count reservations per time slot

http://sqlfiddle.com/#!9/b729e5/1
In my reservation-system users should be able to sign up to multiple events at different time slots, each time slot has a limit of participants.
How can I connect these two tables in one MySQL-Query to get the number of places reserved per time slot of an event?
This data will be sent to php to show the user on a reservation page which time slot has free places and which is already booked up.
you may change the design of the reservations Table to be like this :
reservation_id | user_id | date_of_reservation | event_id | time_slot
then multiple reservations can be multiple rows for the same user ,
then link the two tables reservations and events using user_id + event_id
to test that your data is gethered correctly :
select r.user_id,r.event_id,r.time_slot,e.title
from reservations r,events e
where r.user_id = e.user_id
after that you can device your SQL easily using group by and count
select count(event_id)
from reservations r
where r.user_id = '<specific user>'
/*if you want*/
and time_slot between '' and ''
Fix your table structure first by adding another table like "EventTimeSlot".
Event -> id | host_id | date_of_event | title | description |
EventTimeSlot -> id | event_id | time_slot_name | max_participants |
Reservation -> id | user_id | date_of_reservation | event_id | time_slot_id |
Then make your question clear as I did not understand what you meant by "to get the number of places reserved per time slot of an event?".

MySQL statement pulling too many rows

I have two data tables that I am querying using an Inner Join statement. one of the tables pulls daily company stock information and consequently has a row for each company for each day the market is open. The other table holds qualitative data about the company and only has one row per company.
I am trying to pull the most recent stock information and pair it with the qualitative company information in an HTML table. I have everything working except that it is still pulling every row of daily info for the company rather than the most recent.
Can someone assist with this query?:
$query = "SELECT daily_info.Day,
daily_info.Prev_close,
stocks.Symbol,
stocks.Company,
stocks.Description
FROM daily_info
INNER JOIN stocks ON daily_info.Symbol = stocks.Symbol
ORDER BY daily_info.Day, stocks.Company LIMIT 43
";
Example:
Table 1: Daily_info
Day | Symbol | Company | Prev Close
06/15/14 | CRM | Salesforce | $52.34
06/15/14 | AMZN | Amazon | $342.65
06/16/15 | CRM | Salesforce | $55.24
06/16/14 | AMZN | Amazon | $349.64
Table 2: Stock
Symbol | Company | Description
CRM | Salesforce.com | This is a cloud based CRM company
AMZN | Amazon.com | This is an ecommerce company
Output:
Company | Symbol | Prev Close | Description
Amazon.com | AMZN | $349.64 | This is an ecommerce company
Salesforce.com | CRM | $55.24 | This is a cloud based CRM company
I don't think MySQL supports LAG functions so you'll need to do a subquery to get the last date for each Symbol, then INNER join on that result. Something like this should work:
$query = "
SELECT l.Company, l.Symbol, l.Prev_Close, r2.Description
FROM Daily_info as l
INNER JOIN
(SELECT Symbol, MAX(Prev_close) as last_date FROM Daily_info GROUP BY Symbol) AS r
ON (l.Symbol=r.Symbol AND l.Prev_close=r.last_date)
INNER JOIN Stock as r2
ON (l.Symbol=r2.Symbol)
";

mysql sub query to get latest comment from two tables

I have been working on a query to get summarised list of communications like an inbox that shows conversations
below are my tables
users
company | contact_person | pic_small
alerts
comment_id | user_id | poster_id | timestamp
activity
comment_id | user_id | comment | timestamp
comments
comment_id | user_id | comment | timestamp
This is what I have so far which works ok although I need help with one aspect.
SELECT alerts.comment_id,
alerts.user_id,
alerts.poster_id,
alerts.active,
MAX(alerts.timestamp) AS maxTime,
users.contact_person,
users.company,
users.pic_small
FROM alerts
LEFT JOIN users ON users.user_id = alerts.poster_id
WHERE alerts.user_id = %s
GROUP BY alerts.comment_id
ORDER BY maxTime DESC
Comments are held in the activity and comments tables and I need to somehow join the last (newest) comment from either the activity or comments table (depending which is newer)
How do I add this to my above query, below is what I am trying to achieve

Mysql Query from Query results

I am making something like an announcement board that requires readers to acknowledge that they read it, and was wondering if there is a more efficient way of doing this.
I have 3 Tables on MySQL side:
+-----------------+ +-----------------+ +-----------------+
| Announcements | | Acknowledgement | | User |
+-----------------+ +-----------------+ +-----------------+
| announce_id | | ack_id | | user_id |
| announce_msg | | announce_id | | user_name |
| ... | | user_id | | ... |
+-----------------+ +-----------------+ +-----------------+
When a user "reads" the announcement (by clicking a button), Acknowledgment table will be inserted with the Announcement ID and User ID. When a second user "reads" the same announcement, Acknowledgement table will be inserted again with same Announcement ID and the second User ID and so on...
+--------------------------------+
| Acknowledgement |
+--------+-------------+---------+
| ack_id | announce_id | user_id |
+--------+-------------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 4 |
| 3 | 1 | 3 |
| 4 | 3 | 1 |
| 5 | 3 | 6 |
| 6 | 3 | 2 |
+--------+-------------+---------+
Now to the problem. On the front end, when I list all the announcements on a page, I would have to first query for all the announcements. Then, for each announcement, I would have to do another query for all the users that have read this announcement.
$sql = "select * from Announcements";
$result = $pdo->query($sql);
while ($row = $result->fetch())
{
$announce_id = $row['announce_id'];
$announce_msg = $row['annouce_msg'];
$readers = "";
$sql2 = "select u.user_name from Acknowledgement as a INNER JOIN User as u where announcement_id =".$annouce_id;
$result2 = $pdo->query($sql);
while ($row2 = $result2->fetch())
{
$readers .= $row2['user_name'].", ";
}
echo "id:".$annouce_id.", message:".$announce_msg.", Readers:".$readers;
}
So if there 10 announcements on the page, there will be 10 sub-queries for each announcement. What I have now does the job right now... but what if there is 1000 announcements? Then there will be 1000 sub-queries? Sounds like the database will be really hammered. So I'm hoping there is a better way of doing this.
Also, if 1000 people in the user table reads all 1000 announcements, the acknowledgement table will have 1000x1000 entries. seems like the acknowledgement table will become really really long. Will that be a problem as time goes by?
This is a really rough example of what I'm trying to do but it did take me a long time to write all this. If more details is needed let me know.
There is a better way. You can use a single query with group_concat:
select a.*, group_concat(u.user_name separator ', ') as AllUsers
from Announcements a join
Acknowledgement ak
on a.Announce_Id = ak.Announce_Id join
User u
on u.user_ID = ak.User_ID
group by a.announce_id
This uses the MySQL feature of hidden columns to group by only one column (announce_id) but still pull in a bunch of other columns with no aggregations (everything else pulled in by the "*").
If your purpose here is to filter out the announcements that your current user has read, you can do this an entirely different way. Instead of querying for every announcement, and then finding out all the users that have read those announcements and examining those results to find ones that your use has read and trimming them from the displayed list, you can just query in one go for everything a particular user (or list of users) have not yet read.
Change your query to this:
SELECT * FROM Announcements WHERE Announce_id NOT IN (SELECT ANNOUNCE_ID FROM Acknowledgement WHERE User_ID = <INSERT USER ID HERE>)
That should return all Announcement rows that this particular user has not yet acknowledged. If you change that final WHERE clause to be WHERE User_ID IN () then you can specify a list of user IDs.
EDIT: Given the comment you posted above, you could use this query to get all announcements that have been read by no one:
SELECT * FROM Announcements WHERE Announce_id NOT IN (SELECT ANNOUNCE_ID FROM Acknowledgement WHERE User_ID IN (SELECT User_ID FROM User))
The logic for putting together a query to find announcements that haven't been read by someone (if not everyone) is escaping me right this second.
EDIT THE SECOND: Every announcement, and everyone who has and has not read it, requires use of a different kind of join that you've used above, a FULL OUTER JOIN. Unfortunately MySQL doesn't have that feature IIRC, but it can be simulated with a union query
SELECT A.*, ACK.*, U.* FROM Announcements AS A
INNER JOIN Acknowledgement AS ACK ON A.Announce_ID = ACK.Announce_ID
LEFT OUTER JOIN User AS U ON ACK.User_ID = U.User_ID
WHERE U.User_ID IS NOT NULL
UNION ALL
SELECT A.*, ACK.*, U.* FROM Announcements AS A
LEFT OUTER JOIN Acknowledgement AS ACK ON A.Announce_ID = ACK.Announce_ID
RIGHT OUTER JOIN User AS U ON ACK.User_ID = U.User_ID
I think that should do it. No facilities to test at the moment, of course.

MySQL Data from 3 tables

I have three MySQL tables which relate to a messaging system. The schema and sample data is shown below for each table relating to my question:
`messages`:
+----+---------+----------+
| id | subject | senddate |
+----+---------+----------+
| 1 | Testing | 12344555 |
+----+---------+----------+
`message_senders`:
+------------+---------+---------+
| message_id | user_id | trashed |
+------------+---------+---------+
| 1 | 1 | 1 |
+------------+---------+---------+
`message_recipients`:
+------------+---------+------+----------+---------+
| message_id | user_id | type | readdate | trashed |
+------------+---------+------+----------+---------+
| 1 | 1 | to | 12344555 | 1 |
+------------+---------+------+----------+---------+
| 2 | 1 | cc | 12344555 | 1 |
My question is how would I select all messages sent by or received by a user, where the trashed parameter is set to 1, without selecting duplicate messages. For example, consider the following scenario:
I want to get the message IDs for all messages trashed by user_id 1, but I don't want to retrieve duplicate IDs (in the data above for example, user_id 1 is the sender AND recipient of message_id 1. I don't want to return the message_id of 1 twice, but want to get all messages for that user.
I think I need to use a combination of JOIN and UNION, but my brain isn't functioning after a long day of PHP!
Try this and see if it only returns one row for each message in the messages table...
select * from messages
left join message_senders on messages.id = message_senders.message_id
left join message_recipients on messages.id = message_recipients.message_id
where message_senders.trashed = 1 or message_recipients.trashed = 1 and messages.user_id = <value>
Assuming you got the query in your mind - I'll just hint you can use DISTINCT keyword to force the DB not to return duplicates.
I am sure you can work it out yourself, because self-conclusions work the best.
Also, a piece of advice - always store dates as datetime or date instead of int. You avoid daylight savings time problems and you can use various date functions provided by MySQL.
Dates are saved internally as 4 byte integers, same as int fields.
Here you go, give this a shot:
select distinct m.id
from messages m
left join message_senders s on m.id = s.message_id
left join message_recipients r on m.id = r.message_id
where ((s.user_id = 1 and s.trashed = 1) or (r.user_id = 1 and r.trashed = 1))
The solution that leaps to my mind is to use a UNIONd subquery:
SELECT DISTINCT * FROM messages WHERE id IN (
(SELECT message_id FROM message_recipients WHERE user_id=1 AND trashed=1)
UNION
(SELECT message_id FROM message_senders WHERE user_id=1 AND trashed=1)
)

Categories