Get maximum id if there are parent id otherwise get all results - mysql

I have a table and having the following data
CREATE TABLE IF NOT EXISTS `tbl_ticket` (
`id` int(9) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) NOT NULL,
`ticket_title` varchar(250) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=16 ;
--
-- Dumping data for table `tbl_ticket`
--
INSERT INTO `tbl_ticket` (`id`, `parent_id`, `ticket_title`) VALUES
(1, 0, 'tyty'),
(2, 0, 'testing'),
(3, 0, 'test from ticket'),
(4, 0, 'test ticket'),
(5, 0, 'test ticket'),
(6, 0, 'test ticket'),
(7, 0, 'test ticket'),
(8, 5, 'test ticket'),
(9, 0, '1 Ticket'),
(10, 0, '2Ticket'),
(11, 2, 'ticket2'),
(12, 2, 'ticket1'),
(13, 0, 'title 1234'),
(14, 0, 'titles 1234'),
(15, 14, 'sample 1234');
I need to return all rows where id is not present in parent id from the table.
Also if id is present in the parent_id column, I want to get the row having the highest id which matches the parent_id.
i.e. I need to return rows with id 1, 3,4,6,7,8,9,10, 12,13, 15.
I tried this sql
SELECT `id` , `parent_id`
FROM `tbl_ticket`
WHERE id NOT
IN (
SELECT parent_id
FROM tbl_ticket
)
but it returns value 11 also, instead it should return 12 which is the row having highest id with parent_id =2

Assuming the 5 in your expected output is a typo, as 5 appears in the parent_id field for id=8, you can get your result by the union of two simple queries.
select t1.id
from tbl_ticket t1
where not exists (
select 1 from tbl_ticket
where parent_id = t1.id
)
and parent_id = 0
union all
select max(id)
from tbl_ticket
where parent_id <> 0
group by parent_id
order by id asc
Fiddle here
The query is in two parts. the first part gets all the tickets that are not present in another tickets parent_id field, and which themselves do not have a parent (parent_id = 0).
The second part of the query looks at those tickets that DO have a parent (parent_id <> 0), and for each group of tickets that share the same parent_id, selects the one with the max id.
The results are then combined with a union to give a single result set. Since the two result sets are mutually exclusive, we can use union all to skip over the duplicate check.

If I understand correctly, you can do this with not exists rather than combining two separate queries. The advantage is that no duplicate elimination is needed (as is needed when you use union):
select t.*
from tbl_ticket t
where not exists (select 1
from tbl_ticket t2
where t2.parent_id = t.id
) or
not exists (select 1
from tbl_ticket t2
where t2.parent_id = t.id and t2.id > t.id
);
The first gets all rows that have no parents. The second gets all rows with the maximum id for a parent.
For best performance, you want an index on tbl_ticket(parent_id, id).

Related

MySQL: ORDER BY and GROUP BY together

I recently upgraded to MySQL 5.7.22 and my query stopped working. I have two tables "items" and "packages" where I'm trying to output a row for each item including a column for the package with the minimum price per unit, but ignore packages that have a price per unit set to 0.
Here's a minimal sample of tables and data:
CREATE TABLE `items` (
`id` int(11) NOT NULL
);
CREATE TABLE `packages` (
`item_id` int(11) NOT NULL,
`price_per_unit` float(16,6) DEFAULT 0
);
INSERT INTO `items` (`id`) VALUES
(1),
(2),
(3);
INSERT INTO `packages` (`item_id`, `price_per_unit`) VALUES
(1, 0.45),
(1, 0),
(1, 0.56),
(1, 0.34);
Here's the query:
SELECT
*
FROM
(
SELECT
items.id,
NULLIF(pkgs.ppu, 0) AS mppu
FROM
items
LEFT JOIN
(
SELECT
item_id,
price_per_unit AS ppu
FROM
packages
) AS pkgs ON pkgs.item_id = items.id
ORDER BY
IFNULL(mppu, 9999)
) X
GROUP BY
X.id
I was setting the zero values to null and then bumping their values to be much higher during the ordering. There must be a better way (especially since this method doesn't work any longer).
The expected output for this data is:
id mppu
1 0.34
2 null
3 null
I think your query is a bit too complex. What about this?
SELECT i.id,IFNULL(Min(p.price_per_unit), 'NONE')
FROM items i
LEFT JOIN packages p
ON ( i.id = p.item_id )
WHERE p.price_per_unit > 0
OR p.price_per_unit IS NULL
GROUP BY i.id
See this fiddle. I used this data:
INSERT INTO `items` (`id`) VALUES
(1),(2),(3);
INSERT INTO `packages` (`item_id`, `price_per_unit`) VALUES
(1, 0.45),
(1, 0),
(1, 0.56),
(1, 0.34),
(2, 9.45),
(2, 0),
(2, 0.56),
(2, 0.14);
And got this result:
id IFNULL(min(p.price_per_unit),'None')
1 0.340000
2 0.140000
3 None
Agree with GL,
SELECT * FROM GROUP BY
is not predictable .
i will rewrite the query with :
SELECT a.*,b.min_price_per_unit
FROM items a
LEFT JOIN (
SELECT item_id
,min(CASE
WHEN price_per_unit = 0
THEN 9999
ELSE price_per_unit
END) AS min_price_per_unit
FROM packages
GROUP BY item_id
) b ON a.id = b.item_id;

(SQL) how to get the number of the entry in the queue?

I have this table:
CREATE TABLE tickets (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
topic VARCHAR(64) NOT NULL,
priority TINYINT NOT NULL default 1,
created DATETIME);
INSERT INTO tickets (id, topic, priority, created) VALUES
(1, 'kin23oi5n2', 3, '2018-05-27 16:00:54'),
(2, 'ewbrv32b3', 2, '2018-05-27 16:00:54'),
(3, 'aern23523', 1, '2018-05-27 16:01:15'),
(4, '4bt43t3', 5, '2018-05-27 16:01:15'),
(5, '32n523n5235n235t3n4', 4, '2018-05-27 16:01:39'),
(6, 'e2nr2n23n5235n23n523', 3, '2018-05-27 16:01:39');
I'm running a query:
SELECT * FROM `tickets` order by `priority` ASC, `created` desc;
How do I get the row number with id 5 in this result using sql?
update:
SELECT count(*) as pos FROMticketsWHERE ...;
As a result, we obtain pos
I try to make that my result was similar to this but contained number of a line of the necessary row(id 5 for example).
in other words I want to get the number of the queue entry
If someone is interested, here is the answer
SELECT num FROM
(SET #row_number = 0;
SELECT
(#row_number:=#row_number + 1) AS num, id
FROM
tickets order by `priority` ASC, `created` desc) as tmp_table WHERE id = 6;
But now there was another question. How is this implemented in Larabel 5.6?
UP
And he did it.

get rows from a table where value of field x is maximum

I have two tables myTable and myTable2 in a mysql database:
CREATE TABLE myTable (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
number INT,
version INT,
date DATE
) ENGINE MyISAM;
INSERT INTO myTable
(`id`, `number`, `version`, `date`)
VALUES
(1, '123', '1', '2016-01-12'),
(2, '123', '2', '2016-01-13'),
(3, '124', '1', '2016-01-14'),
(4, '124', '2', '2016-01-15'),
(5, '124', '3', '2016-01-16'),
(6, '125', '1', '2016-01-17')
;
CREATE TABLE myTable2 (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
myTable_id INT
) ENGINE MyISAM;
INSERT INTO myTable2
(`id`, `myTable_id`)
VALUES
(1, 1),
(2, 1),
(3, 2),
(4, 2),
(5, 3),
(6, 3),
(7, 4),
(8, 4),
(9, 4),
(10, 5),
(11, 6)
;
The field myTable2.myTable_id is a foreign key of myTable.Id.
I would like to get all the rows from myTable where myTable2.myTable_id = myTable.Id and the value of the field version in myTable is the maximum for every corresponding value for the field number in myTable.
I tried something like this:
SELECT
*
FROM
myTable,
myTable2
WHERE
myTable.version = (SELECT MAX(myTable.version) FROM myTable)
But the above query does not return the correct data. The correct query should output this:
Id number version date
2 123 2 2016-01-13
5 124 3 2016-01-16
6 125 1 2016-01-17
Please help!
One way to do this is to get the max version for each number in myTable in a derived table and join with that:
SELECT DISTINCT
m.*
FROM
myTable m
JOIN
myTable2 m2 ON m.id = m2.myTable_id
JOIN
(
SELECT number, MAX(version) AS max_version
FROM myTable
GROUP BY number
) AS derived_table
ON m.number = derived_table.number
AND m.version = derived_table.max_version
With your sample data this produces a result like this:
id number version date
6 125 1 2016-01-17
5 124 3 2016-01-16
2 123 2 2016-01-13
your Query is logically wrong. Here is the correct one
SELECT
*
FROM
myTable,
myTable2
WHERE
(myTable.version,myTable.number) in
(SELECT MAX(myTable.version),number FROM myTable group by number)
and myTable.id=myTable2.id
Here is the sqlfiddle http://sqlfiddle.com/#!9/74a67/4/0
This is the query posted for the previous edited question
SELECT * FROM myTable
inner join myTable2 on myTable.id = myTable2.mytable_id
WHERE (version, number) in
(SELECT MAX(version), number FROM myTable group by number)
Try this solution with using subquery simply as:
# Selecting desired result..
SELECT t1.id, t1.number, t1.version, t1.date
FROM myTable As t1 JOIN
# subquery to select max version and its corresponding
# number form myTable
(SELECT number, max(version) As max_ver FROM myTable
GROUP BY number
) As t2 ON t1.number = t2.number and t1.version = t2.max_ver
# Now checking for foreign key..
WHERE t1.id IN (SELECT mytable_id FROM myTable2);
Was it helpful..

How to select row pairs that match certain criteria in mysql

I have a table of things. Here is a simplified structure:
CREATE TABLE `things` (
`thing_id` int(11) NOT NULL AUTO_INCREMENT,
`thing_group` int(11) NOT NULL,
`thing_status` int(1) NOT NULL DEFAULT '0'
);
There are 2 types of things. Primary, which would have thing_id = thing_group and secondary, which would having a unqiue thing_id but the same thing_group as the primary item.
INSERT INTO `things` (`thing_id`, `thing_group`, `thing_status`) VALUES
(1, 1, 0),
(2, 1, 1),
(3, 3, 1),
(4, 3, 0),
(5, 5, 1),
(6, 5, 1),
(7, 7, 0),
(8, 7, 0),
(9, 9, 1),
(10, 9, 1),
I have thousands of these pairs.
thing_status can be 0 for either the primary or the secondary (or both), but I want to select ONLY a pair (at random) that has thing_status = 1 both for primary and secondary thing.
So from the sample data I provided, it should only return pairs which are either thing_id 5 and 6, or 9 and 10 (at random)
Hard part:
Some things can just have the primary thing only, and no secondary. The query should still return those and treat them equally to things that come in pairs.
Am i better off doing 2 queries or a convoluted single query?
Group your rows by thing_group and select those where the number of rows is the same as the sum of thing_status. Join the resulting set back to the original table on thing_group to obtain the actual rows corresponding to the groups. So:
SELECT
t.thing_id,
t.thing_group
FROM things t
INNER JOIN (
SELECT thing_group
FROM things
GROUP BY thing_group
HAVING COUNT(*) = SUM(thing_status)
ORDER BY RAND()
LIMIT 1
) g ON t.thing_group = g.thing_group
Not so hard, maybe the random part is a bit more tricky:
select *
from things
where
thing_group = (select thing_group
from things
where thing_status = 1
group by thing_group
having count(thing_id) = 2
limit 1)
limit 1
My intuition says that you should use 2 queries with a UNION ALL. But... with MySQL it's not always clear what works and what doesn't.
I believe that this query does what you want though:
SELECT t1.thing_id, t1.group_id
FROM things t1
LEFT JOIN things t2
ON t2.thing_id = t2.thing_group
AND t1.thing_id != t2.thing_id
WHERE (
t1.thing_id = t1.thing_group
AND t2.thing_id IS NULL
) OR (
t1.thing_group = t2.thing_id
AND t1.thing_id != t1.thing_group
)
GROUP BY t1.thing_id, t1.group_id

Best way to update MySQL table with random values from another table

For this (pseudo code) example I have two tables in MySQL:
member { id, name }
names { name }
There are 100 members in member and 10 names. I want to use a random name from names to update the member table. So far I've got this, but, not sure if there is a better method to achieve it.
UPDATE member SET name = (SELECT name FROM names ORDER BY RAND() LIMIT 1);
The code will be executed from a script so I'm looking to avoid functions etc.
Thanks in advance.
You could avoid ordering by rand() by adding id column to your names table and using:
UPDATE member SET name = (SELECT name FROM names WHERE id=floor(1 + rand()*10 ) );
With only 10 names the result won't be much faster, but you would see the difference if you wanted to choose from a bigger set of names as sorting by rand() starts being inefficient quite fast and you do it for every row in members.
Update:
Seems like rand() inside where gives unpredictable results.
Use this one instead:
UPDATE member m1
JOIN ( select id, floor(1+rand()*10) as rnd from member ) m2 on m1.id=m2.id
JOIN names n on n.id = m2.rnd
SET m1.name=n.name
Number of rows affected may vary, if random name matches the one already in the table it doesnt count as update.
Tried to improve piotrm's solution. Seems it works;-)
CREATE TABLE member (
id INT(11) NOT NULL AUTO_INCREMENT,
name VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (id)
);
CREATE TABLE names (
id INT(11) NOT NULL AUTO_INCREMENT,
name VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (id)
);
INSERT INTO member VALUES
(1, NULL),
(2, NULL),
(3, NULL),
(4, NULL),
(5, NULL),
(6, NULL),
(7, NULL),
(8, NULL),
(9, NULL),
(10, NULL),
(11, NULL),
(12, NULL),
(13, NULL),
(14, NULL),
(15, NULL);
INSERT INTO names VALUES
(1, 'text1'),
(2, 'text2'),
(3, 'text3'),
(4, 'text4'),
(5, 'text5'),
(6, 'text6'),
(7, 'text7'),
(8, 'text8'),
(9, 'text9'),
(10, 'text10');
UPDATE
member m1
JOIN (SELECT id, #i:=FLOOR(1 + RAND() * 10), (SELECT name FROM names n WHERE n.id = #i) name FROM member) m2
ON m1.id = m2.id
SET
m1.name = m2.name;