Mysql limit joined table results by previous table column - mysql

I have three tables: test_composite, tests, questions. TestComposite belongs to Test. Test has many Questions.
create table test_composite
(
id bigint unsigned auto_increment
primary key,
id_include_test bigint unsigned not null,
questions_quantity smallint unsigned not null,
)
create table tests
(
id bigint unsigned auto_increment
primary key,
name varchar(128) not null,
)
create table questions
(
id bigint unsigned auto_increment
primary key,
question text not null,
test_id bigint unsigned not null,
)
The question is how to make this query work:
select questions.*
from test_composite
inner join tests on test_composite.id_include_test = tests.id
inner join questions on questions.id in (
select id
from questions
where tests.id = questions.test_id limit test_composite.questions_quantity
);
This query is expected to join test_composite and tests tables and then join questions with limit scope. Therefore everything comes to using previous table data in current table query.
Problem is that limit test_composite.questions_quantity isn't allowed.
Is there any way to do it?
PS. Mysql version 5.6.
I know a little about lateral join, but it is not supported in 5.6 version.

Related

Multiple indexing in a table mysql

I have a table structure like this
`CREATE TABLE `like_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sender_id` int(11) NOT NULL,
`receiver_id` int(11) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `index_user` (`sender_id`,`receiver_id`))`
I have indexed both sender_id and receiver_id. If I try to query this
`Select * from like_user where sender_id = 10`
The index works fine but on the other way around it doesn't.
`Select * from like_user where receiver_id = 11`
How can I make the index work on both the conditions.
The use case is that sender_id is the one who is liking a user and the person who sender id is liking is stored in receiver_id. So If sender wants to see all the users he likes, then indexing works, but if the receiver_id wants to see which senders have liked him, indexing stops working. how we can resolve it?
Only prefix can be used. Postfix cannot. I think that two separate indices, one by sender and another by receiver, will be reasonable:
CREATE TABLE `like_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sender_id` int(11) NOT NULL,
`receiver_id` int(11) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY (`sender_id`),
KEY (`receiver_id`)
);
One of these indices will be used for each table copy. For example, for
SELECT *
FROM like_user t1
JOIN like_user t2 ON t1.sender_id = t2.receiver_id;
the first table copy (t1) will use KEY (`sender_id`) whereas another table copy will use KEY (`receiver_id`).

Mysql count number of result with join

I have 2 tables.
CREATE TABLE $media_table (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`options` longtext DEFAULT NULL,
`order_id` int(11) unsigned DEFAULT NULL,
`player_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`))
CREATE TABLE $category_table (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`category` varchar(300) DEFAULT NULL,
`media_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`))
I get id, options, category for rows matching category 'foo','bar'. I also use limit to get only x number of results.
SELECT mt.id, mt.options, ct.category
FROM $media_table as mt
LEFT JOIN $category_table as ct
ON mt.id = ct.media_id
WHERE mt.player_id = %d AND ct.category IN ('foo','bar')
GROUP BY ct.media_id
ORDER BY mt.order_id
LIMIT $limit
This works as intended. But I dont know how to get total number of results?
I tried this but the count is not correct.
SELECT COUNT(mt.id), ct.category
FROM $media_table as mt
LEFT JOIN $category_table as ct
ON mt.id = ct.media_id
WHERE mt.player_id = %d AND ct.category IN ('foo','bar')
GROUP BY ct.media_id
Where I select all results without the limit (in my previous query) the count is correct.
If I had only one table with primary key id I would do this to get count:
SELECT COUNT(id) FROM table
I dont know how to apply the same to my query.
Edit: I found my answer here select count(*) from select
Question 1: Are you looking at the raw results of the query using a tool like phpMyAdmin or MySQL WorkBency or what?
Question 2: Will the ultimate query results be delivered to the client via a web browser or what?
Answer 1: "The SUM() function returns the total sum of a numeric column."
SELECT SUM(column_name) FROM table_name WHERE condition;
Answer Possibility 2: If the results will be delivered in a web browser you should be able to use PHP or some other server side language like MS Active Server Pages to add up he "COUNT" field of each result.
Answer Possibility 3: Alternative 1: Export the results to a CVS file and import into a spreadsheet.
Maybe some of these suggestions will get the wheels turning and help you find the solution you are looking for.

Subquery processing more rows than necessary

I am optimising my queries and found something I can't get my head around.
I am using the following query to select a bunch of categories, combining them with an alias from a table containing old and new aliases for categories:
SELECT `c`.`id` AS `category.id`,
(SELECT `alias`
FROM `aliases`
WHERE category_id = c.id
AND `old` = 0
AND `lang_id` = 1
ORDER BY `id` DESC
LIMIT 1) AS `category.alias`
FROM (`categories` AS c)
WHERE `c`.`status` = 1 AND `c`.`parent_id` = '11';
There are only 2 categories with a value of 11 for parent_id, so it should look up 2 categories from the alias table.
Still if I use EXPLAIN it says it has to process 48 rows. The alias table contains 1 entry per category as well (in this case, it can be more). Everything is indexed and if I understand correctly therefore it should find the correct alias immediately.
Now here's the weird thing. When I don't compare the aliases by the categories from the conditions, but manually by the category ids the query returns, it does process only 1 row, as intended with the index.
So I replace WHERE category_id = c.id by WHERE category_id IN (37, 43) and the query gets faster:
The only thing I can think of is that the subquery isn't run over the results from the query but before some filtering is done. Any kind of explanation or help is welcome!
Edit: silly me, the WHERE IN doesn't work as it doesn't make a unique selection. The question still stands though!
Create table schema
CREATE TABLE `aliases` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`lang_id` int(2) unsigned NOT NULL DEFAULT '1',
`alias` varchar(255) DEFAULT NULL,
`product_id` int(10) unsigned DEFAULT NULL,
`category_id` int(10) unsigned DEFAULT NULL,
`brand_id` int(10) unsigned DEFAULT NULL,
`page_id` int(10) unsigned DEFAULT NULL,
`campaign_id` int(10) unsigned DEFAULT NULL,
`old` tinyint(1) unsigned DEFAULT '0',
PRIMARY KEY (`id`),
KEY `product_id` (`product_id`),
KEY `category_id` (`category_id`),
KEY `page_id` (`page_id`),
KEY `alias_product_id` (`product_id`,`alias`),
KEY `alias_category_id` (`category_id`,`alias`),
KEY `alias_page_id` (`page_id`,`alias`),
KEY `alias_brand_id` (`brand_id`,`alias`),
KEY `alias_product_id_old` (`alias`,`product_id`,`old`),
KEY `alias_category_id_old` (`alias`,`category_id`,`old`),
KEY `alias_brand_id_old` (`alias`,`brand_id`,`old`),
KEY `alias_page_id_old` (`alias`,`page_id`,`old`),
KEY `lang_brand_old` (`lang_id`,`brand_id`,`old`),
KEY `id_category_id_lang_id_old` (`lang_id`,`old`,`id`,`category_id`)
) ENGINE=InnoDB AUTO_INCREMENT=112392 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
SELECT ...
WHERE x=1 AND y=2
ORDER BY id DESC
LIMIT 1
will be performed in one of several ways.
Since you have not shown us the indexes you have (SHOW CREATE TABLE), I will cover some likely cases...
INDEX(x, y, id) -- This can find the last row for that condition, so it does not need to look at more than one row.
Some other index, or no index: Scan DESCending from the last id checking each row for x=1 AND y=2, stopping when (if) such a row is found.
Some other index, or no index: Scan the entire table, checking each row for x=1 AND y=2; collect them into a temp table; sort by id; deliver one row.
Some of the EXPLAIN clues:
Using where -- does not say much
Using filesort -- it did a sort, apparently for the ORDER BY. (It may have been entirely done in RAM; ignore 'file'.)
Using index condition (not "Using index") -- this indicates an internal optimization in which it can check the WHERE clause more efficiently than it used to in older versions.
Do not trust the "Rows" in EXPLAIN. Often they are reasonably correct, but sometimes they are off by orders of magnitude. Here is a better way to see "how much work" is being done in a rather fast query:
FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';
With the CREATE TABLE, I may have suggestions on how to improve the index.

Find all distinct alternate usernames by an ip in a single query

I'm trying to build a query that will take a player_id, find all distinct ip addresses logged for that person, and pull a list of distinct other player_ids that share each ip.
Example schema:
CREATE TABLE IF NOT EXISTS `ips` (
`ip_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ip` int(10) unsigned NOT NULL,
PRIMARY KEY (`ip_id`)
);
CREATE TABLE IF NOT EXISTS `joins` (
`join_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`player_id` int(10) unsigned NOT NULL,
`ip_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`join_id`)
);
The ips table records every unique ip I see, a players table records each unique player (not important for this) and the joins table records each time they connect.
Doing this with two queries was my first idea, and that works and is speedy - but I'd really like to make this work with a single query. I tried a query that uses a subquery but that takes forever to complete.
What's the most efficient way to query for this?
How does this work for you? http://sqlfiddle.com/#!2/133c6/1
SELECT DISTINCT
joins2.player_id,
ips.ip
FROM joins
JOIN ips
ON ips.ip_id = joins.ip_id
JOIN joins AS joins2
ON joins2.ip_id = ips.ip_id
AND joins2.player_id != #player_id
WHERE joins.player_id = #player_id

Database search with multi joins

I have a MySQL database and I want to perform a little bigger search.
I have about 10k records in one of the tables and It's expected to grow, but slowly.
The biggest problem is that to perform the search I have to make a query with 4 JOINS which I think causes the search to be slow.
So here is some example struct:
[table records]
id INT unsigned PRIMARY KEY auto_increment
description text
label INT unsigned
type INT unsigned
price DECIMAL
[table records_labels]
id INT unsigned PRIMARY KEY auto_increment
label varchar
[table records_types]
id INT unsigned PRIMARY KEY auto_increment
type varchar
[table records_serial]
id INT unsigned PRIMARY KEY auto_increment
serial varchar
record INT unsigned
[table records_barcode]
id INT unsigned PRIMARY KEY auto_increment
barcode varchar
record INT unsigned
So here is how the things run:
I run a query which selects records.id, records.description, records.price, records_labels.label, records_types.type, records_serial.serial, records_barcode.barcode;
So the full query is like this:
SELECT records.id, records.description, records.price, records_labels.label, records_types.type, records_serial.serial, records_barcode.barcode FROM records JOIN records_labels ON records_labels.id = records.label JOIN records_types ON records_types.id = records.type LEFT JOIN records_serial ON records_serial.record = record.id LEFT JOIN records_barcode ON records_barcode.record = record.id WHERE records_serial.serial LIKE %SEARCH_TERM% OR records_barcode.barcode LIKE %SEARCH_TERM%
I think that the solution here is indexing I guess, but I'm not very familiar with it.
So shortly, how to speed up and optimize query of this kind?
indexing records (OPTIONAL, BUT RECOMENDED)
CREATE INDEX ilabel ON records (`label`);
CREATE INDEX itype ON records (`type`);
fixing records_label
ALTER TABLE records_label MODIFY label INT(10) UNSIGNED NULL;
CREATE INDEX ilabel ON records_label (`label`);
fixing records_types
ALTER TABLE records_types MODIFY `type` INT(10) UNSIGNED NULL;
CREATE INDEX itype ON records_types (`type`);
the search
SELECT r.id, r.description, r.price, rl.label,
rt.`type`, records_serial.`serial`, records_barcode.barcode
FROM records r
INNER JOIN records_labels rl ON rl.id = r.label
INNER JOIN records_types rt ON rt.id = r.`type`
WHERE
r.id IN (
SELECT rs.record
FROM records_serial rs
WHERE rs.`serial` LIKE '%SEARCH_TERM%'
)
OR
r.id IN (
SELECT rb.record
FROM records_barcode rb
WHERE rb.barcode LIKE '%SEARCH_TERM%'
);
There is no much what I can do for your where clause. the Like %% kills any sort of performance if you keen to change it for something like this LIKE 'SEARCH_TERM%', then you could create the index below
CREATE INDEX iserial ON records_serial (`serial`(10));
CREATE INDEX ibarcode ON records_barcode (`barcode`(10));
It could be improved even more but with theses changes I believe you achieve what you are looking for. ;-)