I have the following two tables:
Table: user_has_academy_team
Columns:
user_id int(11) PK
academy_team_id int(11) PK
timestamp datetime
Table: user_has_academy_module
Columns:
id int(11) AI PK
user_id int(11)
module_id int(11)
academy_team_id int(11)
academy_id int(11)
sort_number int(11)
is_complete int(11)
score_to_pass int(11)
is_open int(11)
deadline datetime
module_version_id int(11)
waiting_approval int(11)
I wish to find all the records in the table user_has_academy_module where academy_team_id and user_id does not match in user_has_academy_team
You are looking for a NOT IN (or NOT EXISTS) clause: records from one table that have no match in another:
select *
from user_has_academy_module
where (user_id, academy_team_id) not in
(select user_id, academy_team_id from user_has_academy_team);
In a comment to another answer you say you want to delete those records from user_has_academy_module, which is about the same query, only with delete instead of select:
delete
from user_has_academy_module
where (user_id, academy_team_id) not in
(select user_id, academy_team_id from user_has_academy_team);
SELECT a.* FROM user_has_academy_team a
LEFT JOIN user_has_academy_module b
ON a.user_id = b.user_id AND a.academy_team_id = academy_team_id
WHERE b.user_id IS NULL or b.academy_team_id IS NULL
Should give you the result (I believe, since you haven't posted sample and expected). However I feel that your table structure leaves much to be desired. If you had a auto_increment primary key on your user user_has_academy_team table instead of the composite primary key, and referred that in your other table, life would be a lot easier.
Try this:
SELECT m.* FROM user_has_academy_module AS m
LEFT JOIN user_has_academy_team as t
ON m.user_id = t.user_id
AND m.academy_team_id = t.academy_team_id
WHERE t.user_id IS NULL
That's how it works: you try to join 2 tables on one ore more fields, when the fields of the second table are NULL you know that the tables don't match on those fields.
Related
I have an SQL query on tables having a lot of rows. So this query runs for a very long time. How can I optimize this query?
These tables already have indexes on id and friend_id
SELECT u.id, u.first, u.last,
group_concat(u2.first, " " , u2.last) MyFriends
FROM Users u
INNER JOIN Friends f ON f.user_id = u.id
INNER JOIN Users u2 ON u2.id = f.friend_id
GROUP BY u.id;
These are the table structures:
CREATE TABLE Users (
id int(10) unsigned NOT NULL,
first varchar(50) DEFAULT NULL,
last varchar(50) DEFAULT NULL,
city varchar(20) DEFAULT NULL,
country varchar(20) DEFAULT NULL,
Age tinyint(3) unsigned NOT NULL,
KEY users_idx_id (id))
ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE Friends (
user_id int(10) unsigned NOT NULL,
friend_id int(10) unsigned NOT NULL,
KEY idx_friends (friend_id))
ENGINE=InnoDB DEFAULT CHARSET=latin1;
A many-to-many mapping table (Friends) needs improved indexes. Drop all the indexes you have now and add
PRIMARY KEY(user_id, friend_id),
INDEX(friend_id, user_id)
More discussion: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
Age is a moving target. Think about a better way to store that.
There are about 6 countries with names longer than 20. "Saint Vincent and the Grenadines" is 32.
As for cities, 'Poselok Uchebnogo Khozyaystva Srednego Professionalno-Tekhnicheskoye Uchilishche Nomer Odin' is 91 chars.
ALTER TABLE Friends
DROP INDEX idx_friends,
ADD PRIMARY KEY(user_id, friend_id),
ADD INDEX(friend_id, user_id);
Every table should have a PRIMARY KEY:
ALTER TABLE Users
DROP INDEX users_idx_id,
ADD PRIMARY KEY(user_id)
Read about AUTO_INCREMENT.
The "execution plan" can be had by running EXPLAIN SELECT .... However it won't provide many clues in this case.
If there is a result matching the value of "parent_id" in my "table1" and "table2" tables, I want to get the number of rows in the "table1" table.
But the SQL query takes too long.
There are 100 thousand rows in table1.
There are 40 thousand rows in table2.
A table data file for you to try
See: https://pastebin.pl/view/raw/ddf8c467
Table Structure
CREATE TABLE table1 (id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
parent_id INT(11) UNSIGNED NOT NULL ,
tes1 INT(1) NOT NULL , PRIMARY KEY (id)) ENGINE = MyISAM;
CREATE TABLE table2 (id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
parent_id INT(11) UNSIGNED NOT NULL ,
tes2 INT(1) NOT NULL , PRIMARY KEY (id)) ENGINE = MyISAM;
SQL Query I use
SELECT COUNT(A.id) AS total
FROM table1 A
LEFT JOIN table2 B ON A.parent_id = B.parent_id
WHERE B.id IS NOT NULL
create a index on parent_id on table B and use INNODB if possible.
you can also use inner join
SELECT COUNT(A.id) AS total
FROM table1 A
INNER JOIN table2 B ON A.parent_id = B.parent_id;
I have the following query:
select *
from user_interests
join interests using(interest_id)
where tag in('running', 'biking')
and user_id != 1;
Note: The in() values are built dynamically using php, so their could be one value or 100 values, each passed in via user browser $_GET.
My user_interests table is pretty simple, as it is just a list of the interest_id and the user_id.
CREATE TABLE `user_interests` (
`interest_id` INT(10) UNSIGNED NOT NULL,
`user_id` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`interest_id`, `user_id`)
);
My interests table is a simple table that holds a list of different types of interests (running, biking, etc.)
CREATE TABLE `interests` (
`interest_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`tag` VARCHAR(50) NOT NULL,
`category_id` TINYINT(3) UNSIGNED NOT NULL,
PRIMARY KEY (`interest_id`),
UNIQUE INDEX `tag` (`tag`)
);
Since each user can have many interests, how can I search the database for users that have all of the interests in the list. For the query above that would be running and biking. My query just gets people who have at least one of the above, how can I make it so it gets users who have all of the queried interests?
So if a user has 5 interests, and I pass in 2 and they have those 2 in their list of 5, their profile should be returned. If they only have 1 of the 2 passed in there profile should not be returned.
Here is a Fiddle: http://sqlfiddle.com/#!9/29ea2
I would try that
SELECT *
FROM user_interests
WHERE user_interests.user_id IN (
SELECT ui.user_id
FROM user_interests ui
left join interests i using(interest_id)
WHERE i.tag IN ($php_array)
^ PHP
and ui.user_id != 1
GROUP BY ui.user_id
HAVING COUNT(i.tag) >= count($php_array)
^ PHP
)
Fiddle
I have a table that contains many informations:
CREATE TABLE sequences (
`id` INT(11) NOT NULL DEFAULT '0',
`name` TEXT NULL,enter code here
`nbrlsu` BIGINT(20) NULL DEFAULT NULL,
`nbrits` BIGINT(20) NULL DEFAULT NULL,
`nbrco1` BIGINT(20) NULL DEFAULT NULL,
`nbrrcbl` BIGINT(20) NULL DEFAULT NULL,
`nbrmatk` BIGINT(20) NULL DEFAULT NULL,
`nbrsequences` BIGINT(20) NULL DEFAULT NULL,
`parent_id` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
);
I want to create a table based on sum of columns in the first table
for exemple I want to know te number of elements that have the same parent_id and has numbersequences>0
and I want to know for each type of sequences the number of rows that contains information:
SELECT parent_id ,
Classification,count(id) as nbrspecies,
SUM(nbrsequences) ,
SUM(nbrco1),
SUM(nbrits),
SUM(nbrlsu),
SUM(nbrrcbl),
SUM(nbrmatk)
FROM dashboard_specimen
GROUP BY parent_id
and I have an other kind of queries:
SELECT parent_id ,
count(id) as co1
FROM dashboard_specimen
WHERE nbrco1>0
GROUP BY parent_id ;
and
SELECT parent_id ,
count(id) as nbrspecies
FROM dashboard_specimen
WHERE nbrsequences>0
GROUP BY parent_id
and other types like this
and my goal in the end is to insert this information into an other table with insert select
like this:
INSERT INTO bold_namestats (id,
name,
numberofstrains,
numberofsequences,
numberofco1,
numberofits,
numberoflsu,
numberofrbcl,
numberofmatk)
SELECT parent_id ,
Classification,
count(id) as nbrspecies,
SUM(nbrsequences) ,
SUM(nbrco1),
SUM(nbrits),
SUM(nbrlsu),
SUM(nbrrcbl),
SUM(nbrmatk)
FROM dashboard_specimen
GROUP BY parent_id
I don't know if there is a simple way to do this with temp tables or something like this
If I understand well, you could do a subquery for each column you want to populate, filtering each subquery for an id.
INSERT INTO bold_namestats (id,
name,
numberofstrains,
numberofsequences,
numberofco1,
numberofits,
numberoflsu,
numberofrbcl,
numberofmatk)
select parent_id, (*select1* where parent_id=...), (*select2* where parent_id=...), ... , (*selectn* where parent_id=...)
from dashboard_specimen
group by parent_id
where select1, select2, ... , selectn are the different queries you have.
Finally I have resolved my problem using join and temp tables
INSERT INTO bold_namestats (_id,numberofstrains, numberofsequences,numberofco1,numberofits,numberoflsu,numberofrbcl,numberofmatk,numberstrainswithco1,numberstrainswithseq)
SELECT a._id ,a.numberofstrains,a.numberofsequences ,a.numberofco1,a.numberofits,a.numberoflsu,a.numberofrbcl,a.numberofmatk,b.numberofstrainswithco1,c.numberofstrainswithseq FROM bold_temp_namestats a left join bold_strainswithco1 b on a._id=b.parent_id left join bold_strainswithseq c on a._id=c.parent_id union
SELECT a._id ,a.numberofstrains,a.numberofsequences ,a.numberofco1,a.numberofits,a.numberoflsu,a.numberofrbcl,a.numberofmatk,b.numberofstrainswithco1,c.numberofstrainswithseq FROM bold_temp_namestats a right join bold_strainswithco1 b on a._id=b.parent_id left join bold_strainswithseq c on a._id=c.parent_id ;
this query is used to replace full outer join so I fill 3 tables with data and after that I insert with joinin result with left and right join and union the result to get full lines in the end
I modeled a small database for easier explanation:
CREATE TABLE bands (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(120) NULL,
PRIMARY KEY(id)
)
TYPE=InnoDB;
CREATE TABLE albums (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
band_id INTEGER UNSIGNED NOT NULL,
album_name VARCHAR(120) NULL,
rating INTEGER UNSIGNED NULL,
insertion_date TIMESTAMP NULL,
PRIMARY KEY(id),
INDEX albums_FKIndex1(band_id),
FOREIGN KEY(band_id)
REFERENCES bands(id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
)
TYPE=InnoDB;
Now, pretending that we already have some bands and many albums registered in their respective tables, I want to select ONLY the last inserted album from each registered band.
PS: I have to use the "album.insertion_date" field to determine which album is the last inserted.
Try joining the two tables and filtering by insertion_date and band:
SELECT al.*
FROM albums al
INNER JOIN bands b ON al.band_id=b.id
WHERE al.insertion_date=(
SELECT max(insertion_date)
FROM albums
WHERE band_id=b.id
)
Try this one:
select b.name, a.album_name, a.isertion_date
from bands b, albums a
where a.band_id = b.id
and a.insertion_date = (select max(a1.insertion_date) from albums a1 where a1.band_id = b.id)
Considering that you have the albums' ids to be AUTO_INCREMENT and the possibility for the insertion_date to be NULL(as it is the default value), using insertion_date to determine the results is not the smartest thing to do but ... there you go:
SELECT DISTINCT band, last_album, insertion_date
FROM (
SELECT bands.name AS band, albums.album_name AS last_album, albums.insertion_date
FROM bands
JOIN albums ON bands.id=albums.band_id
ORDER BY albums.insertion_date DESC
) t1
GROUP BY band;