Joining with the most recently-entered value - mysql

I have a table that shows work tickets, basically like this:
TABLE "ticket"
ticket_id INT UNSIGNED AUTO_INCREMENT
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ticket_name VARCHAR(64)
Then I have a table showing the possible list of status codes for a ticket:
TABLE "status_ticket"
status_id INT UNSIGNED AUTO_INCREMENT
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
status_name VARCHAR(64)
And I have one more table that logs the history of the statuses that a ticket has or has had:
TABLE "xref_ticket_status"
xref_id INT UNSIGNED AUTO_INCREMENT
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ticket_id INT UNSIGNED NOT NULL
status_id INT UNSIGNED NOT NULL
When the ticket is created, an entry is made in the ticket table for the ticket, and then an entry is made in the xref_ticket_status table assigning a status to the ticket by linking the ticket table to the status_ticket table. When the status changes, a new entry is made in the xref_ticket_status table to reflect the new status. This way, I have a history of each status that a ticket has had and when it was assigned. The most recent entry for any given ticket_id in the xref_ticket_status table is the ticket's current status.
I'm not sure how I would join these three tables together to get a ticket's current status. Essentially, I want to join the ticket table with the xref_ticket_status table where ticket_id matches but for the newest created column in the xref_ticket_status.
Thanks!

This should do the trick:
select ti.ticket_id,ti.ticket_name,
xr.created, st.status_name
from ticket ti
inner join xref_ticket_status xr
on(ti.ticket_id = xr.ticket_id)
inner join status_ticket st
on(xr.status_id = st.status_id)
group by ti.ticket
order by xr.xref_id desc;
===============Update==================
select * from
(select ti.ticket_id,ti.ticket_name,
xr.created, st.status_name
from ticket ti
inner join xref_ticket_status xr
on(ti.ticket_id = xr.ticket_id)
inner join status_ticket st
on(xr.status_id = st.status_id)
group by ti.ticket
order by xr.xref_id desc)cs
order by cs.ticket_name desc; //or asc

The following gets the most recent status_id and ticket_id from xref_ticket_status:
select xts.*
from xref_ticket_status xts
where not exists (select 1
from xref_ticket_status xts2
where xts2.ticket_id = xts.ticket_id and
xts2.created > xts.created
);
The logic is: "Get me all rows from xref_ticket_status where there is no row with the same ticket id and a more recent created date". The would be the last date for each ticket. And note that you can also use xref_id for the comparison rather than the date.
The rest is just joining the tables together and choosing your desired columns:
select t.*, ts.*, xts.created
from xref_ticket_status xts join
ticket t
on xts.ticket_id = t.ticket_id join
ticket_status ts
on xts.status_id = ts.status_id
where not exists (select 1
from xref_ticket_status xts2
where xts2.ticket_id = xts.ticket_id and
xts2.created > xts.created
);

Related

mysql get max status ordered by date

I'm facing MySQL query problem with ordering grouped rows.
Tables:
statuses (
  id int primary key auto_increment,
name varchar(30)
);
orders (
id int primary key auto_increment,
order_code varchar(10),
order_date timestamp default current_timestamp,
user_id int -- fk to users
);
orders_statuses (
id int primary key auto_increment,
status_changed timestamp default current_timestamp,
order_id int, -- fk to orders(id)
status_id int -- fk to orders(id)
);
Now, I need to join them in ONE query (this is important due to database object limitation with my PHP software - no sub-queries allowed, not even union) and get all possible orders, but only the latest status change as current status.
Statuses from status table may be inserted randomly - there is no way to use max function on status_id field where order_id (currently id 1 is new, 2 is canceled, 4 is paid etc.).
Grouping by order_id (id in orders or order_code) always returns first available status inserted into orders_statuses, even if ordered by date.
I've also tried using having status_changed=max(status_changed), but it won't work.
So my question is:
How should I group them by order_id to get last, not first status name available for each row?
// edit
Here is the code I'm trying to move from my Workbench to PHP DB Object:
select
(select l.name from statuses l where l.id = (select t.status_id from orders_statuses t where t.order_id = oh.id order by t.status_changed desc limit 1)) as status,
oh.order_code,
oh.id,
oh.order_total,
su.name as order_user,
oh.order_date as ordered,
max(os.status_changed) as updated,
oh.id as uid
from orders oh
left join orders_statuses os on (oh.id = os.order_id)
left join users su on (oh.shop_user_id = su.id)
group by
oh.id
order by
oh.order_date desc,
os.created desc
So basically I need simpler query to understand it and put it into $this->db->use('table', on condition)->...->select(array(fields))
// edit
After hours of thinking I can tell I'm lacking know-how. This problem can be solved by using
SELECT
status_id,
created,
order_id
FROM
orders_statuses
GROUP BY
order_id,
status_id
ORDER BY
created DESC;
and transforming it to return just one status_id for each order_id with max created within single order_id group - this is where I need your help.

MySQL query using different tables and filters

I have one table called 'vacancies' which has a 'vacancy_id' PK. It looks like this:
create table vacancies
(
vacancy_id int not null auto_increment,
org_id int not null,
name varchar(255) not null comment 'title',
vacancy_visibility_start_date datetime comment 'vacancy visibility date, when it needs to be active on the website',
vacancy_visibility_end_date datetime,
primary key (vacancy_id)
);
Following this I have a couple of other tables which are linked to this one.
create table vacancy_calendar
(
vacancy_calendar_id int not null auto_increment,
vacancy_id int,
date_from datetime not null,
date_to datetime not null,
primary key (vacancy_calendar_id)
);
create table vacancy_interests
(
vacancy_id int,
interest_id int
);
create table vacancy_skills
(
vacancy_id int,
skill_id int
);
All of these tables can contain multiple rows for the same vacancy_id.
My page has different filters which I want to process via AJAX.
I want to have one line per vacancy containing all data I need + it has to match my filtering criteria. However I am not sure how my query has to look like in order to retrieve the result I am looking for.
It is possible to filter on 'interest_id' , 'skill_id', 'date_from' and 'date_to'.
I started with the following query but I am stuck very fast:
SELECT v.*, vi.interest_id
FROM `vacancies` as v
INNER JOIN `vacancy_interests` as vi on v.vacancy_id = vi.vacancy_id
GROUP BY v.vacancy_id
This query will only return me 1 interest_id for a vacancy, even if the vacancy has 3 interest_id rows in the vacancy_interest table. If I remove the GROUP BY statement I will get 3 rows for the same vacancy which is not what I want either.
Ideally I would want the interest_id's to be each in a separate column or in the same field separated by comma's. Or if there are any other possibilities/suggestions feel free to share!
You can use group_concat for get interest_id separated by comma
SELECT v.*, group_concat(vi.interest_id)
FROM `vacancies` as v
INNER JOIN `vacancy_interests` as vi on v.vacancy_id = vi.vacancy_id
GROUP BY v.vacancy_id
Referring to you comment about add where eg:
You can add where condition
SELECT v.*, group_concat(vi.interest_id)
FROM `vacancies` as v
INNER JOIN `vacancy_interests` as vi on v.vacancy_id = vi.vacancy_id
INNER JOIN `vacancy_skills` as vs ON vs.vacancy_id = v.vacancy_id
WHERE vs.skill_id IN (4) AND vi.interest_id IN (1,3)
GROUP BY v.vacancy_id
In this case the gorup_concat is applied on the resulting rows .. because group by perform the related action on the selected resulting rows .

MySQL - Select data from relational tables A, B, C, D, E if record was not found on Table F

I have 6 tables in my database: users, courses, users_courses, lectures, users_lectures and attends. In the headline, I referred to them as A, B, C, D, E and F.
I'm creating an attending system for lecture as a part of my school project and I need to run a query that selects only those lectures where users haven't yet attended.
users table holds information about user. Its' table structure is following:
id INT(10) USINGNED PRIMARY KEY AUTO_INCREMENT
name VARCHAR(255)
email VARCHAR(255)
password VARCHAR(60)
created_at TIMESTAMP
updated_at TIMESTAMP
courses table is a parent table for lectures that holds information about courses where lectures belongs to. Its' table structure is following:
id INT(10) UNSIGNED AUTO_INCREMENT
name VARCHAR(255)
created_at TIMESTAMP
updated_at TIMESTAMP
users_courses is a relationship table between users and courses. It links users to courses. Its' table structure is following:
user_id INT(10) UNSIGNED FOREIGN KEY
course_id INT(10) UNSIGNED FOREIGN KEY
lectures table holds information about upcoming lectures users are supposed to attend. It's table structure is following:
id INT(10)
starting_at DATETIME
ending_at DATETIME
course_id INT(10) UNSIGNED FOREIGN KEY
created_at TIMESTAMP
updated_at TIMESTAMP
users_lectures table is a relationship table between users and lectures. It links lectures to users. Its' table structure is following:
user_id INT(10) UNSIGNED FOREIGN KEY
lecture_id INT(10) UNSIGNED FOREIGN KEY
attends table holds information about attending user's user's id, lecture's id and timestamps for created_at and updated_at. Its' table structure is following:
user_id INT(10) UNSIGNED FOREIGN KEY
lecture_id INT(10) UNSIGNED FOREIGN KEY
created_at TIMESTAMP
updated_at TIMESTAMP
Question:
Is it possible, with one query, to select only those upcoming lectures where users haven't yet attended?
Here's what I've tried, but it doesn't return any rows:
// PHP
$stmt = $pdo->prepare("
SELECT
courses.name AS course,
lectures.id AS id,
lectures.starting_at,
lectures.ending_at,
users.name AS user_name,
users_lectures.user_id
FROM lectures
LEFT JOIN users_courses ON users_courses.course_id = lectures.course_id
LEFT JOIN courses ON courses.id = users_courses.course_id
LEFT JOIN users_lectures ON users_lectures.lecture_id = lectures.id
LEFT JOIN users ON users.id = users_lectures.user_id
LEFT JOIN attends ON attends.lecture_id = users_lectures.lecture_id
WHERE users_lectures.user_id = :user_id
AND lectures.ending_at > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 1 MINUTE)
AND lectures.ending_at > DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MINUTE)
AND attends.lecture_id != users_lectures.lecture_id
AND attends.user_id != :user_id
ORDER BY lectures.starting_at DESC");
Am I missing something or thinking the whole structure in a wrong way?
Sorry for providing such a long text, but I wanted to be as specific as possible. Any help would be much appreciated :)
First of all you need to start with the subject of what you want to return. That would be users. So this will be your first table to select from (Note here: use AS to rename tables for brevity):
Select *
From users AS u
Next link together the data you want. Depending on how specific you want to be will depend on how many joins you will need to make (e.g. courseID vs CourseName).
Now assuming we want lots of human readable data such as names we will link the following tables.
LEFT OUTER JOIN users_lectures AS ul ON u.id = ul.userid
LEFT OUTER JOIN lectures AS l ON l.id = ul.lectureid
LEFT OUTER JOIN courses AS c ON c.id = l.courseid
LEFT OUTER JOIN attends AS a ON a.userid = u.id AND a.lectureid = l.id
And to top it off, to find people who didn't attend, they will not be in the attends table so we just check for people where the values are null for attends.
WHERE a.userid IS NULL
As a side note, you won't get any results with this in the WHERE clause
AND attends.lecture_id != users_lectures.lecture_id
Because you are joining with this statement
LEFT JOIN attends ON attends.lecture_id = users_lectures.lecture_id
Which contradict each other

How can I select and order rows from one table based on values contained in another?

I am currently working with two tables.
status:
id INT(4) AUTO_INCREMENT
username VARCHAR(20)
text LONGTEXT
datetime VARCHAR(25)
attachment VARCHAR(11)
timestamp VARCHAR(50)
friends:
id INT(2) AUTO_INCREMENT
added INT(1)
userto VARCHAR(32)
userfrom VARCHAR(32)
I would like to add the option for a user to filter statuses for only their friend's statuses, and display them with the newest one first. By this I mean most recent status, not newest per friend, which I suspect can be done with:
ORDER BY status.id DESC
How would I order the statuses based on the statuses of the users on the person's friends list?
Well, without any sample data it would be hard to do this for sure, but I would walk through it this way.
The statuses we want to show (regardless of order) are those the user is friends with. So, let's get the friends of Adam for example:
SELECT DISTINCT userto
FROM friends
WHERE userfrom = 'Adam'
UNION
SELECT DISTINCT userfrom
FROM friends
WHERE userto = 'Adam';
At this moment, I should point out that in your friends table the usernames are VARCHAR(32) and in the status table they are VARCHAR(20), I would assume they should be the same.
So, now you can filter the status based on whether or not the username is in the above subquery, and you could order by id descending, assuming you add them in order, but the best way would be to order by the timestamp on the status:
SELECT *
FROM status
WHERE username IN
(SELECT DISTINCT userto
FROM friends
WHERE userfrom = 'Adam'
UNION
SELECT DISTINCT userfrom
FROM friends
WHERE userto = 'Adam')
ORDER BY datetime DESC;
EDIT
I would also rethink your variables for datetime and timestamp, as well as rethink the name. While these are valid, they are also reserved words (DATETIME and TIMESTAMP are data types in MySQL). A possible reconstruction of your status table could be:
id INT(4) AUTO_INCREMENT
username VARCHAR(32)
statusText LONGTEXT
statusPostDate DATETIME
attachment VARCHAR(11)
The DATETIME variable will hold both the date and time portions for you.
Try this
SELECT status.*
FROM status
JOIN friends
ON status.username = friends.userto
WHERE friends.userfrom = '$username'
UNION
SELECT status.*
FROM status
JOIN friends
ON status.username = friends.userfrom
WHERE friends.userto = '$username'
ORDER BY status.id DESC;
this query loads statuses from usernames on either end of the friendship relationship that includes $username. Union is used to glue two lists top to bottom. Join is used to glue two lists side by side and align them on the author's username.

Get data from 3 table

I have three tables in my database first one is account_group, next is ledger and last one is account_receipt,
account_group has fields group_id and group_name, and this group_id is mapped to ledger, ledger table contains fields ledger_name and group_id. and in the last table account_receipt has fields ledger_id and receipt_amount.
so what I need is I want to get receipt details when group_id is given.I cant write the query for this.any help would be appreciated.
schema::
account_group::
id int(11)
group_name varchar(60)
ledger ::
id int(11)
group_id varchar(60)
ledger_name varchar(60)
account_receipt::
id int(11)
ledger_id int(11)
amount float
receipt_date date
Try the following query. It uses JOINs to connect the various tables, column aliases and table aliases.
SELECT
ag.id group_id,
l.id ledger_id,
l.ledger_name ledger_name
ar.id receipt_id,
ar.amount amount,
ar.receipt_date receipt_date
FROM account_receipt ar
INNER JOIN ledger l ON ar.ledger_id = l.id
INNER JOIN account_group ag ON ag.id = l.group_id AND ag.id = <given group id>;
Three table joining example has been given below
select * from Table_A A
join Table_B B on B.id=A.id
join Table_C C on C.id=A.id