LIMIT number of rows in a JOIN between MySQL tables - mysql

What I have
I have the following two tables in a MySQL database (version 5.6.35).
CREATE TABLE `Runs` (
`Name` varchar(200) NOT NULL,
`Run` varchar(200) NOT NULL,
`Points` int(11) NOT NULL
) DEFAULT CHARSET=latin1;
INSERT INTO `Runs` (`Name`, `Run`, `Points`) VALUES
('John', 'A08', 12),
('John', 'A09', 3),
('John', 'A01', 15),
('Kate', 'A02', 92),
('Kate', 'A03', 1),
('Kate', 'A04', 33),
('Peter', 'A05', 8),
('Peter', 'A06', 14),
('Peter', 'A07', 5);
CREATE TABLE `Users` (
`Name` varchar(500) NOT NULL,
`NumberOfRun` int(11) NOT NULL
) DEFAULT CHARSET=latin1;
INSERT INTO `Users` (`Name`, `NumberOfRun`) VALUES
('John', 2),
('Kate', 1),
('Peter', 3);
ALTER TABLE `Runs`
ADD PRIMARY KEY (`Run`);
What is my target
John have Users.NumberOfRun=2, so I will extract the 2 top records from Runs table
Kate have Users.NumberOfRun=1, so I will extract the 1 top record from Runs table
Peter have Users.NumberOfRun=3, so I will extract the 3 top records from Runs table
I would like to came to the following result
+-------+-----+--------+
| Name | Run | Points |
+-------+-----+--------+
| John | A01 | 15 |
| John | A08 | 12 |
| Kate | A02 | 92 |
| Peter | A06 | 14 |
| Peter | A05 | 8 |
| Peter | A07 | 5 |
+-------+-----+--------+
What I have tried
First of all, if it was SQL Server I would use ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ) AS [rn] function to the Runs table and then make a JOIN with the Users table on Users.NumberOfRun<=[rn].
I have read this document but it seems that PARTITONING in MySQL it is available since version 8.X, but I am using the 5.6.X version.
Finally, I have tried this query, based on this Stackoverflow answer:
SELECT t0.Name,t0.Run
FROM Runs AS t0
LEFT JOIN Runs AS t1 ON t0.Name=t1.Name AND t0.Run=t1.Run AND t1.Points>t0.Points
WHERE t1.Points IS NULL;
but it doesn't give me the row number, which is essentially for me to make a JOIN as described above.
SQL Fiddle to this example.

A combination of 'group_concat' and 'find_in_set', followed by the filtering using the position returned by 'find_in_set' will do the job for you.
GROUP_CONCAT will sort the data in descending order of points first.
GROUP_CONCAT(Run ORDER BY Points DESC)
FIND_IN_SET will then retrieve the number of rows you want to include in the result.
FIND_IN_SET(Run, grouped_run) BETWEEN 1 AND Users.NumberOfRun
The below query should work for you.
SELECT
Runs.*
FROM
Runs
INNER JOIN (
SELECT
Name, GROUP_CONCAT(Run ORDER BY Points DESC) grouped_run
FROM
Runs
GROUP BY Name
) group_max ON Runs.Name = group_max.Name
INNER JOIN Users ON Users.Name = Runs.Name
WHERE FIND_IN_SET(Run, grouped_run) BETWEEN 1 AND Users.NumberOfRun
ORDER BY
Runs.Name Asc, Runs.Points DESC;

Related

How do I select the max(timestamp) from a relational mysql table fast

We are developing a ticket system and for the dashboard we want to show the tickets with it's latest status. We have two tables. The first one for the ticket itself and a second table for the individual edits.
The system is running already, but the performance for the dashboard is very bad (6 seconds for ~1300 tickets). At first we used a statemant which selected 'where timestamp = (select max(Timestamp))' for every ticket. In the second step we created a view which only includes the latest timestamp for every ticket, but we are not able to also include the correct status into this view.
So the main Problem might be, that we can't build a table in which for every ticket the lastest ins_date and also the latest status is selected.
Simplyfied database looks like:
CREATE TABLE `ticket` (
`id` int(10) NOT NULL,
`betreff` varchar(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ticket_relation` (
`id` int(11) NOT NULL,
`ticket` int(10) NOT NULL,
`info` varchar(10000) DEFAULT NULL,
`status` int(1) NOT NULL DEFAULT '0',
`ins_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`ins_user` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `ticket` (`id`, `betreff`) VALUES
(1, 'Technische Frage'),
(2, 'Ticket 2'),
(3, 'Weitere Fragen');
INSERT INTO `ticket_relation` (`id`, `ticket`, `info`, `status`, `ins_date`, `ins_user`) VALUES
(1, 1, 'Betreff 1', 0, '2019-05-28 11:02:18', 123),
(2, 1, 'Betreff 2', 3, '2019-05-28 12:07:36', 123),
(3, 2, 'Betreff 3', 0, '2019-05-29 06:49:32', 123),
(4, 3, 'Betreff 4', 1, '2019-05-29 07:44:07', 123),
(5, 2, 'Betreff 5', 1, '2019-05-29 07:49:32', 123),
(6, 2, 'Betreff 6', 3, '2019-05-29 08:49:32', 123),
(7, 3, 'Betreff 7', 2, '2019-05-29 09:49:32', 123),
(8, 2, 'Betreff 8', 1, '2019-05-29 10:49:32', 123),
(9, 3, 'Betreff 9', 2, '2019-05-29 11:49:32', 123),
(10, 3, 'Betreff 10', 3, '2019-05-29 12:49:32', 123);
I have created a SQL Fiddle: http://sqlfiddle.com/#!9/a873b6/3
The first three Statements are attempts that won't work correct or way too slow. The last one is the key I think, but I don't understand, why this gets the status wrong.
The attempt to create the table with latest ins_date AND status for each ticket:
SELECT
ticket, status, MAX(ins_date) as max_date
FROM
ticket_relation
GROUP BY
ticket
ORDER BY
ins_date DESC;
This query gets the correct (latest) ins_date for every ticket, but not the latest status:
+--------+--------+----------------------+
| ticket | status | max_date |
+--------+--------+----------------------+
| 3 | 1 | 2019-05-29T12:49:32Z |
+--------+--------+----------------------+
| 2 | 0 | 2019-05-29T10:49:32Z |
+--------+--------+----------------------+
| 1 | 0 | 2019-05-28T12:07:36Z |
+--------+--------+----------------------+
Expected output would be this:
+--------+--------+----------------------+
| ticket | status | max_date |
+--------+--------+----------------------+
| 3 | 3 | 2019-05-29T12:49:32Z |
+--------+--------+----------------------+
| 2 | 1 | 2019-05-29T10:49:32Z |
+--------+--------+----------------------+
| 1 | 3 | 2019-05-28T12:07:36Z |
+--------+--------+----------------------+
Is there a efficient way to select the latest timestamp and status for every ticket in the tiket-table?
Other approach is to think filtering not GROUPing..
Query
SELECT
ticket_relation_1.ticket
, ticket_relation_1.status
, ticket_relation_1.ins_date
FROM
ticket_relation AS ticket_relation_1
LEFT JOIN
ticket_relation AS ticket_relation_2
ON
ticket_relation_1.ticket = ticket_relation_2.ticket
AND
ticket_relation_1.ins_date < ticket_relation_2.ins_date
WHERE
ticket_relation_2.id IS NULL
ORDER BY
ticket_relation_1.id DESC
Result
| ticket | status | ins_date |
| ------ | ------ | ------------------- |
| 3 | 3 | 2019-05-29 12:49:32 |
| 2 | 1 | 2019-05-29 10:49:32 |
| 1 | 3 | 2019-05-28 12:07:36 |
see demo
This query would require a index KEY(ticket, ins_date, id) to get max performance..
One solution would be to use a subquery to compute the latest insert date for each ticket, and then to join the results with the original table, like:
SELECT t.ticket, t.status, t.ins_date
FROM ticket_relation t
INNER JOIN (
SELECT ticket, max(ins_date) max_ins_date
FROM ticket_relation
GROUP BY ticket
) x ON t.ticket = x.ticket AND t.ins_date = x.max_ins_date
For better performance with this query, you want an index on (ticket, ins_date).
Anoter option would be to use a NOT EXISTS condition to ensure that only the latest record is selected, like:
SELECT t.ticket, t.status, t.ins_date
FROM ticket_relation t
WHERE NOT EXISTS (
SELECT 1
FROM ticket_relation t1
WHERE t1.ticket = t.ticket AND t1.ins_date > t.ins_date)
)
NB: when dealing with GROUP BY, all non-aggregated columns must appear in the GROUP BY clause. Else, you will get either an error or unprectictable results (depending on whether server option ONLY_FULL_GROUP_BY is, respectively, enabled or disabled).
If you are able to upgrade to a recent version of mysql (8.0), then window functions can be used to simplify the query and possibly increase its performance, like:
SELECT ticket, status, ins_date
FROM (
SELECT
ticket,
status,
ins_date,
row_number() over(partition by ticket order by ins_date desc) rn
FROM ticket_relation
) x WHERE rn = 1
You can try below query -
SELECT
ticket, status, ins_date as max_date
FROM ticket_relation a
where ins_date in (select max(ins_date) from ticket_relation b where a.ticket=b.ticket)

MySQL storing and querying latest software version

With the following sample table, I want to create a MySQL query that returns the latest version for each of the following fictional applications (based on traditional software version numbering). I am using MySQL version 5.5.17.
I would also consider using a stored function, if a function can be created that makes a more elegant query.
app | major | minor | patch
------+-------+-------+--------
cat | 2 | 15 | 0
cat | 2 | 15 | 1
cat | 2 | 2 | 0
dog | 1 | 0 | 1
dog | 1 | 7 | 2
dog | 3 | 0 | 0
fish | 2 | 2 | 5
fish | 2 | 3 | 1
fish | 2 | 11 | 0
Expected query result:
app | major | minor | patch
------+-------+-------+--------
cat | 2 | 15 | 1
dog | 3 | 0 | 0
fish | 2 | 11 | 0
You can use this sql to create the table called my_table, so you can test.
CREATE TABLE IF NOT EXISTS `my_table` (
`app` varchar(10) NOT NULL,
`major` int(11) NOT NULL DEFAULT '0',
`minor` int(11) NOT NULL DEFAULT '0',
`patch` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `my_table` (`app`, `major`, `minor`, `patch`) VALUES
('cat', 2, 15, 1),
('cat', 2, 15, 0),
('cat', 2, 2, 0),
('dog', 1, 0, 1),
('dog', 1, 7, 2),
('dog', 3, 0, 0),
('fish', 2, 2, 5),
('fish', 2, 3, 1),
('fish', 2, 11, 0);
If you assume that the minor version and patch never go above 1000, you can combine them into a single number major*100000 + minor*1000 + patch. Then you can apply one of the techniques at SQL Select only rows with Max Value on a Column after calculating this for each row.
SELECT m.*
FROM my_table AS m
JOIN (SELECT app, MAX(major*1000000 + minor*1000 + patch) AS maxversion
FROM my_table
GROUP BY app) AS m1
ON m.app = m1.app AND major*1000000 + minor*1000 + patch = maxversion
DEMO
There are three approaches I can think of. And all of them are pretty ugly, and all of them involve subqueries.... 1) use correlated subqueries in SELECT list of a GROUP BY query, 2) use inline view to get max of canonical string concatenation of (zero padded) major_minor_patch 0002_0015_0001, and then either unpack the string representation, or join to table to get matching row, or 3) use a query that orders the rows by app, then by highest version of each app, and a trick (unsupported) with user defined values to flag the "first" row for each app. None of these is pretty.
Here's a demonstration of one approach.
We start with this, to get each app:
SELECT t.app
FROM my_table t
GROUP BY t.app
Next step, get the highest "major" for each app, we can do something like this:
SELECT t.app
, MAX(t.major) AS major
FROM my_table t
GROUP BY t.app
To get the highest minor within that major, we can make that an inline view... wrap it in parens and reference it like a table in another query
SELECT t2.app
, t2.major
, MAX(t2.minor) AS minor
FROM my_table t2
JOIN (
SELECT t.app
, MAX(t.major) AS major
FROM my_table t
GROUP BY t.app
) t1
ON t2.app = t1.app
AND t2.major = t1.major
GROUP BY t2.app, t2.major
To get the highest patch, we follow the same pattern. Using the previous query as an inline view.
SELECT t4.app
, t4.major
, t4.minor
, MAX(t4.patch) AS patch
FROM my_table t4
JOIN ( -- query from above goes here
SELECT t2.app
, t2.major
, MAX(t2.minor) AS minor
FROM my_table t2
JOIN ( SELECT t.app
, MAX(t.major) AS major
FROM my_table t
GROUP BY t.app
) t1
ON t2.app = t1.app
AND t2.major = t1.major
GROUP BY t2.app, t2.major
) t3
ON t4.app = t3.app
AND t4.major = t3.major
AND t4.minor = t3.minor
GROUP BY t4.app, t4.major, t4.minor
That's just an example of one approach.
FOLLOWUP:
For another approach (getting a canonical representation of the version, that is, combining the values of "major", "minor" and "patch" in a single expression so that the result can be "ordered" by that expression to get the highest version), see the answer from Gordon.

Get rolled up sub total and total by joining multiple tables

Extending Rolling up addition using mysql
I can get rolling sum of rows https://stackoverflow.com/users/1529673/strawberry's answer for basic stuff so i tried to extend query by joining multiple tables but 1_keyworddefs.name='K2' is not affected. Getting same answer for 1_keyworddefs.name='K1' and K2.
Working query but directly by specifying 1_bugs.bug_id='2':
SELECT x.*, x.cf1 + x.cf2 sub_total, sum(y.cf1 + y.cf2) total FROM 1_bugs x INNER JOIN 1_bugs y ON y.bug_id <= x.bug_id INNER JOIN 1_keywords ON 1_keywords.bug_id = y.bug_id WHERE (x.bug_date BETWEEN '2016-07-19' AND '2016-07-22') AND (x.bug_id='2') AND (y.bug_status = 'VERIFIED' OR y.bug_status = 'CLOSED') AND (1_keywords.bug_id = x.bug_id) GROUP BY x.bug_id
Exact output (but i want to join table where 1_keywords.bug_id=1_bugs.bug_id matches instead of directly specifying 1_bugs.bug_id='2'):
bug_id bug_date cf1 cf2 bug_status sub_total total
2 2016-07-19 2 1 VERIFIED 3 3
Non-working query by joining tables (expecting answer like above):
SELECT x.*, x.cf1 + x.cf2 sub_total, sum(y.cf1 + y.cf2) total FROM 1_bugs x INNER JOIN 1_bugs y ON y.bug_id <= x.bug_id LEFT JOIN 1_keywords ON 1_keywords.bug_id = y.bug_id LEFT JOIN 1_keyworddefs ON 1_keyworddefs.id=1_keywords.keywordid AND 1_keyworddefs.name='K2' and 1_keywords.bug_id = y.bug_id WHERE (x.bug_date BETWEEN '2016-07-19' AND '2016-07-22') AND (y.bug_status = 'CLOSED' OR y.bug_status = 'VERIFIED') GROUP BY x.bug_id;
Expected:
bug_id bug_date cf1 cf2 bug_status sub_total total
2 2016-07-19 2 1 VERIFIED 3 3
Actual:
bug_id bug_date cf1 cf2 bug_status sub_total total
2 2016-07-19 2 1 VERIFIED 3 3
3 2016-07-22 2 2 CLOSED 4 7
** Here bug_id -> 3 row comes wrongly because 1_bugs.bug.id=1_keywords.bug_id doesn't match and there is no 1_keywords.bug_id='3' present in 1_keywords table.
DDLs:
-- 1_bugs table1 (master table) :
CREATE TABLE `1_bugs` (`bug_id` int(11) NOT NULL, `bug_date` date NOT NULL, `cf1` int(11) NOT NULL, `cf2` int(11) NOT NULL, `bug_status` varchar(200) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `1_bugs` (`bug_id`, `bug_date`, `cf1`, `cf2`, `bug_status`) VALUES (1, '2016-07-19', 3, 2, 'RESOLVED'), (2, '2016-07-19', 2, 1, VERIFIED'), (3, '2016-07-22', 2, 2, 'CLOSED');
-- 1_keywords table2 (having keyword ids):
CREATE TABLE `1_keywords` (`bug_id` int(11) NOT NULL, `keywordid` varchar(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `1_keywords` (`bug_id`, `keywordid`) VALUES (1, 'K1'), (2, 'K2');
-- 1_keyworddefs table3 (having keyword names according to keywordid):
CREATE TABLE `1_keyworddefs` (`id` int(11) NOT NULL, `name` varchar(200) NOT NULL, `description` varchar(200) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `1_keyworddefs` (`id`, `name`, `description`) VALUES (1, 'K1', 'My K1 item'), (2, 'K2', 'My K2 item');
Can someone please point me what i'm doing wrong?
Obviously things where not clear... after long discussions turns out what you wanted was to extract from bugzilla database a list of bugs for specific keyword grouped by date and a sum of cf1 + cf2 and the same sum of only 'opened' and 'verified' bugs.
Here we go
SELECT
b.lastdiffed,
d.name,
d.description,
SUM(IF(b.bug_status IN('CLOSED', 'VERIFIED'), b.cf1 + b.cf2, 0)) AS sub_total,
SUM(b.cf1 + b.cf2) AS running
FROM
bugs AS b
JOIN keywords AS k
ON ( k.bug_id = b.bug_id )
JOIN keyworddefs AS d
ON ( d.id = k.keywordid )
WHERE
1
AND d.name = 'SONY'
GROUP BY (b.lastdiffed)
ORDER BY b.lastdiffed ASC
Giving this result
+------------+------+-------------+-----------+---------+
| lastdiffed | name | description | sub_total | running |
+------------+------+-------------+-----------+---------+
| 2016-05-20 | SONY | sony items | 7 | 7 |
| 2016-06-20 | SONY | sony items | 11 | 17 |
| 2016-06-27 | SONY | sony items | 5 | 5 |
| 2016-06-29 | SONY | sony items | 5 | 5 |
+------------+------+-------------+-----------+---------+
Hope this helps.

JOIN query in MySQL produces wrong result

I have two tables complaints and complaints_reply in my MySQl database. Users can add complaints which are stored in complaints the complaints reply are stored in complaints_reply table. I am trying to JOIN both these table contents on a specific condition. Before I mention what I am trying to get and the problem I faced, I will explain the structure of these two tables first.
NB: The person who adds complaints is complaint owner & person who adds a complaint reply is complaint replier. Complaint owner can also add replies. So he can either be the complaint owner or the complaint replier. The two tables have a one-to-many relationship. A complaint can have more than one complaint reply. member_id in complaint table represents complaint owner & mem_id in complaints_reply represent complaint replier
DESIRED OUTPUT:
Join the two tables and fetch values and show the complaint and complaint’s reply as a single result set. But the condition is kinda tricky. The last added complaint reply from the complaints_reply table should be fetched for the complaint in complaints table in such a way that the complaint owner should not be the complaint replier. I use posted_date & posted_time from complaints_reply table to fetch the last added complaint reply for a complaint & that complaint replier has to be shown in the result set.
So, from the sample data the tables contain now, the output that I should get is:
+------+---------+----------+-------------+-------------------+
| id | title |member_id |last_replier |last_posted_dt |
+------+---------+----------+-------------+-------------------+
| 1 | x | 1000 |2002 | 2015-05-2610:11:17|
| 2 | y | 1001 |1000 | 2015-05-2710:06:16|
+------+---------+----------+-------------+-------------------+
But what I got is:
+------+---------+----------+-------------+-------------------+
| id | title |member_id |last_replier |last_posted_dt |
+------+---------+----------+-------------+-------------------+
| 1 | x | 1000 |1001 | 2015-05-2610:11:17|
| 2 | y | 1001 |2000 | 2015-05-2710:06:16|
+------+---------+----------+-------------+-------------------+
The date is correct, but the returned complaint replier last_replier is wrong.
This is my query.
SELECT com.id,
com.title,
com.member_id,
last_comp_reply.last_replier,
last_comp_reply.last_posted_dt
FROM complaints com
LEFT JOIN
(SELECT c.id AS complaint_id,
c.member_id AS parent_mem_id,
cr.mem_id AS last_replier,
max(cr.posted_dt) AS last_posted_dt
FROM
(SELECT cr.complaint_id,cr.mem_id,c.id,c.member_id,(CONCAT(cr.posted_date,cr.posted_time)) AS posted_dt
FROM complaints_reply cr,
complaints c
WHERE cr.complaint_id=c.id
AND cr.mem_id!=c.member_id
GROUP BY cr.complaint_id,
cr.mem_id,
posted_dt)cr,
complaints c
WHERE cr.complaint_id=c.id
GROUP BY cr.complaint_id,
c.id,
c.member_id) AS last_comp_reply ON com.id=last_comp_reply.complaint_id
Table structure for table complaints
CREATE TABLE IF NOT EXISTS `complaints` (
`id` int(11) NOT NULL,
`title` varchar(500) NOT NULL,
`member_id` int(11) NOT NULL,
`posted_date` date NOT NULL,
`posted_time` time NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
Indexes for table complaints
ALTER TABLE `complaints`
ADD PRIMARY KEY (`id`);
AUTO_INCREMENT for table complaints
ALTER TABLE `complaints`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3;
Dumping data for table complaints
INSERT INTO `complaints` (`id`, `title`, `member_id`, `posted_date`, `posted_time`) VALUES
(1, 'x', 1000, '2015-05-05', '02:06:15'),
(2, 'y', 1001, '2015-05-14', '02:08:10');
Table structure for table complaints_reply
CREATE TABLE IF NOT EXISTS `complaints_reply` (
`id` int(11) NOT NULL,
`complaint_id` int(11) NOT NULL,
`comments` text NOT NULL,
`mem_id` int(11) NOT NULL,
`posted_date` date NOT NULL,
`posted_time` time NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
Indexes for table complaints_reply
ALTER TABLE `complaints_reply`
ADD PRIMARY KEY (`id`);
AUTO_INCREMENT for table complaints_reply
ALTER TABLE `complaints_reply`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=10;
Dumping data for table complaints_reply
INSERT INTO `complaints_reply` (`id`, `complaint_id`, `comments`, `mem_id`, `posted_date`, `posted_time`) VALUES
(1, 1, 'reply1', 2000, '2015-05-08', '02:07:08'),
(2, 1, 'reply2', 2001, '2015-05-06', '06:05:08'),
(3, 1, 'reply3', 1000, '2015-05-14', '02:12:13'),
(4, 2, 'hola', 1000, '2015-05-27', '10:06:16'),
(5, 2, 'hello', 2000, '2015-05-04', '03:09:09'),
(6, 2, 'gracias', 1001, '2015-05-31', '06:12:18'),
(7, 1, 'reply4', 1001, '2015-01-04', '04:08:12'),
(8, 2, 'puta', 1001, '2015-06-13', '06:12:18'),
(9, 1, 'reply5', 1000, '2015-06-01', '04:08:12'),
(10, 1, 'reply next', 2002, '2015-05-26', '10:11:17');
P.S.
To give an idea about what my query is all about, I'll explain the sub query that is used to combine the tables & give result based on the condition: complaint owner should not be the complaint replier is:
SELECT cr.complaint_id,
cr.mem_id,
c.id,
c.member_id,
(CONCAT(cr.posted_date,cr.posted_time)) AS posted_dt
FROM complaints_reply cr,
complaints c
WHERE cr.complaint_id=c.id
AND cr.mem_id!=c.member_id
GROUP BY cr.complaint_id,
cr.mem_id,
posted_dt
And the result for this is:
+--------------+---------+----------+-------------+-------------------+
| complaint_id | mem_id | id |member_id | posted_dt |
+--------------+---------+------- +-------------+-------------------+
| 1 | 1001 | 1 |1000 | 2015-01-0404:08:12|
| 1 | 2000 | 1 |1000 | 2015-05-0802:07:08|
| 1 | 2001 | 1 |1000 | 2015-05-0606:05:08|
| 1 | 2002 | 1 |1000 | 2015-05-2610:11:17|
| 2 | 1000 | 2 |1001 | 2015-05-2710:06:16|
| 2 | 2000 | 2 |1001 | 2015-05-0403:09:09|
+--------------+---------+----------+-------------+-------------------+
member_id here represents complaint owner and mem_id represents complaint replier
The inner query gives the result based on the condition, then everything after this goes haywire. I don't know where I made mistake. The complaint replies added by complaint owner is not fetched in this table. So far so good. Is there any alternative way to get the result from here?
This query gives the result.
SELECT com.id AS complaint_id,
com.member_id AS parent_mem_id,
crep.mem_id AS last_replier,
crl.last_posted_dt
FROM complaints com
LEFT JOIN complaints_reply crep ON com.id=crep.complaint_id
JOIN
(SELECT cr.complaint_id,
max(CONCAT(cr.posted_date,'_',cr.posted_time)) AS last_posted_dt
FROM complaints_reply cr,
complaints c
WHERE cr.complaint_id=c.id
AND cr.mem_id!=c.member_id
GROUP BY cr.complaint_id)crl ON CONCAT(crep.posted_date,'_',crep.posted_time)=crl.last_posted_dt
AND crep.complaint_id=crl.complaint_id

MySQL NOT IN from another column in the same table

I want to run a mysql query to select all rows from a table films where the value of the title column does not exist anywhere in all the values of another column (collection).
Here is a simplified version of my table with content:
mysql> select * from films;
+----+--------------+--------------+
| id | title | collection |
+----+--------------+--------------+
| 1 | Collection 1 | NULL |
| 2 | Film 1 | NULL |
| 3 | Film 2 | Collection 1 |
+----+--------------+--------------+
Here is my query:
mysql> SELECT * FROM films WHERE title NOT IN (SELECT collection FROM films);
Empty set (0.00 sec)
In this example, I would want to select the rows with titles Film 1 and Film 2, but my query is returning no rows.
Here is the table structure:
CREATE TABLE `films` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(200) NOT NULL DEFAULT '',
`collection` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
SELECT *
FROM films
WHERE title NOT IN (SELECT collection FROM films where collection is not null);
SQLFiddle: http://sqlfiddle.com/#!2/76278/1
Have you tried using NOT EXISTS:
SELECT *
FROM films f1
WHERE NOT EXISTS (SELECT collection
FROM films f2
WHERE f1.title = f2.collection);
See SQL Fiddle with Demo
If you want to use IN then you will want to look for values that are NOT NULL:
SELECT *
FROM films
WHERE title NOT IN (SELECT collection
FROM films
WHERE collection is not null);
See SQL Fiddle with Demo
The result for both is:
| ID | TITLE | COLLECTION |
------------------------------
| 2 | Film 1 | (null) |
| 3 | Film 2 | Collection 1 |
The problem with your current query is that -- stealing from #Quassnoi's answer here:
Both IN and NOT IN return NULL which is not an acceptable condition for WHERE clause.
Since the null value is being returned by your subquery you want to specifically exclude it.
Another option using an outer join
SELECT f.*
FROM films f LEFT OUTER JOIN films ff
ON f.title = ff.collection
WHERE ff.collection IS NULL
Try this please:
SQLFIDDLE DEMO
Query:
select a.id, a.planid
from one a
left join one b
on a.planid <> b.iid
where not (b.iid is null)
group by b.id
;
Results: based on the sample table I used.
ID PLANID
t15 1
j18 2
EDIT TO ADD : HERE WITH OP SCHEMA
select b.id, b.title
from opschema b
inner join opschema a
on b.title <> a.collection
or b.collection <> a.title
group by b.id
;
OP SHCEMA SQLFIDDLE DEMO
ID TITLE
2 Film 1
3 Film 2
CREATE TABLE IF NOT EXISTS `reservation_tables` (
`res_table_id` int(10) NOT NULL AUTO_INCREMENT,
`res_table_name` int(10) NOT NULL,
`date_time` varchar(20) NOT NULL,
`partyhall_id` int(10) NOT NULL,
`flag` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`res_table_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
INSERT INTO `reservation_tables` (`res_table_id`, `res_table_name`, `date_time`, `partyhall_id`, `flag`) VALUES
(1, 1, '2014-08-17 12:00 am', 7, '1'),
(2, 2, '2014-08-17 12:00 am', 7, '1'),
(3, 3, '2014-08-18 12:00 am', 8, '1'),
(4, 4, '2014-08-18 12:00 am', 8, '1'),
(5, 1, '2014-08-25 12:00 am', 12, '1'),
(6, 2, '2014-08-25 12:00 am', 12, '1'),
(7, 3, '2014-08-20 12:00 am', 23, '1'),
(8, 4, '2014-08-20 12:00 am', 23, '1');
Ι had to select available table name for matching date_time
Example select available table_name where date_time = 2014-08-18 12:00 am.
solution query is:
im sure this works well
SELECT distinct res_table_name FROM reservation_tables WHERE `res_table_name` NOT IN
(SELECT `res_table_name` FROM reservation_tables where `date_time` = '2014-08-17 12:00 am')