I have two tables s_filter_values and s_filter_articles.
s_filter_values: s_filter_articles:
| id | value | | articleID | valueID |
|----|-------| |-----------|---------|
| 1 | one | | 1 | 2 |
| 2 | two | | 1 | 3 |
| 3 | three | | 2 | 2 |
With the following statement I count the the occurence of the values
respectively I get the values that are not linked to an article:
SELECT v.*, IFNULL(COUNT(a.articleID), 0) AS counter
FROM s_filter_values AS v
LEFT JOIN s_filter_articles AS a ON v.id = a.valueID
GROUP BY v.id
HAVING counter = 0
In this case, I got
| id | value | counter |
| 1 | one | 0 |
My questions is: How can I use this statement to delete all rows from s_filter_values that are not linked to an article?
I will suggest using NOT EXIST rather than NOT IN.
The NOT EXISTS should perform faster on a large dataset.
There is one key difference between the two constructs: if the subquery returns a NULL in its results then the NOT IN condition will fail, because null is neither equal-to nor not-equal-to any other value.
create table s_filter_values(
id int(10),
`value` varchar(10) );
insert into s_filter_values values ( 1,'one'),( 2,'two'),( 3,'three');
create table s_filter_articles(
articleID int(10),
valueID int(10) );
insert into s_filter_articles values ( 1,2),( 1,3),( 2,2);
DELETE FROM s_filter_values
WHERE NOT EXISTS (SELECT valueID FROM s_filter_articles a where a.valueID= s_filter_values.id);
Demo: https://www.db-fiddle.com/f/7yUJcuMJPncBBnrExKbzYz/84
I would use a subquery to get all IDs that are in the table. Then drop the rows from s_filter_values that are not present.
DELETE FROM s_filter_values WHERE id NOT IN (SELECT DISTINCT valueID FROM s_filter_articles);
A simple sub select should do it:
DELETE FROM `s_filter_values`
WHERE `id` NOT IN SELECT DISTINCT `valueID` FROM `s_filter_articles`
Related
I have 3 tables
User Info
id
name
1
bob
2
jane
3
tom
Locations
id
name
1
Test1
2
Test2
3
Test3
4
Test4
User Locations
userID
locationID
1
1
1
2
2
3
Basically What I am trying to achieve is to pull the location names where the user doesn't have it bound already.
In the above list Bob has 2 locations bounded "test 1" and "test 2" but he doesn't have "test 3" or "test 4" yet. I Only want the data to return test 3 and 4 since those are the only ones Bob doesn't have.
For Jane She only has Test 3 bounded but none of the remaining 3
Originally I had tried this and it somewhat worked. However Every time another user gets an unbounded location the its removed from the list. I'm not sure how I would add the user ID in all this so it's only specific to that user.
SELECT `name` FROM `locations`
WHERE `id` NOT IN (SELECT `locationID` FROM `user_locations`)
Create a cartesain product of the user and locations table (cross join), then using an outer join allows us to find rows that are as yet unmatched in user_locations:
select
user_info.ID AS UserID
, locations.ID AS locationID
from user_info
cross join locations
left outer join user_locations on user_info.id = user_locations.userid
and locations.id = user_locations.locationid
where user_locations.userid IS NULL
and user_info.name = 'bob'
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE user_info(
id INTEGER NOT NULL PRIMARY KEY
,name VARCHAR(4) NOT NULL
);
INSERT INTO user_info(id,name) VALUES (1,'bob');
INSERT INTO user_info(id,name) VALUES (2,'jane');
INSERT INTO user_info(id,name) VALUES (3,'tom');
CREATE TABLE locations(
id INTEGER NOT NULL PRIMARY KEY
,name VARCHAR(5) NOT NULL
);
INSERT INTO locations(id,name) VALUES (1,'Test1');
INSERT INTO locations(id,name) VALUES (2,'Test2');
INSERT INTO locations(id,name) VALUES (3,'Test3');
INSERT INTO locations(id,name) VALUES (4,'Test4');
CREATE TABLE user_locations(
userID INTEGER NOT NULL
,locationID INTEGER NOT NULL
);
INSERT INTO user_locations(userID,locationID) VALUES (1,1);
INSERT INTO user_locations(userID,locationID) VALUES (1,2);
INSERT INTO user_locations(userID,locationID) VALUES (2,3);
Query 1:
select
user_info.ID AS UserID
, locations.ID AS locationID
from user_info
cross join locations
left outer join user_locations on user_info.id = user_locations.userid
and locations.id = user_locations.locationid
where user_locations.userid IS NULL
order by 1,2
Results:
| UserID | locationID |
|--------|------------|
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 2 |
| 2 | 4 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
| 3 | 4 |
Two tables users, relationships in my db.
CREATE TABLE users(
id int primary key auto_increment,
nickname varchar(20),
is_active TINYINT
)
CREATE TABLE relationships(
id int primary key auto_increment,
user_id int,
target_user_id int,
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(target_user_id) REFERENCES users(id)
)
mysql> select * from users;
+----+----------+-----------+
| id | nickname | is_active |
+----+----------+-----------+
| 1 | hide | 1 |
| 2 | john | 1 |
| 3 | ben | 0 |
| 4 | test | 1 |
| 5 | gogo | 1 |
+----+----------+-----------+
mysql> select * from relationships;
+----+---------+----------------+
| id | user_id | target_user_id |
+----+---------+----------------+
| 1 | 1 | 2 |
| 2 | 1 | 4 |
+----+---------+----------------+
I have to extract users.id with certain condition.
I will explain in case of users.id = 1
users.is_active=1
user who does not have relationships via relationships table. you know that in current relationships table, user_id = 1 has 2 rows that target_user_id = 2 and 4. So query result does not contain user_id = 2 and 4.
Using NOT IN, it is pretty simple.
SELECT id FROM users WHERE is_active=1 AND id NOT IN(SELECT target_user_id FROM relationships WHERE user_id=1)
RESULT : 1, 5
Note that there is huge rows in users and relationships.
If I using NOT IN with subquery, it will occur performance issue.
So I think I have to join with foreign key but I don't know how to make query exactly.
Any suggestion, appreciate.
Thanks.
TRY THIS: I am sure LEFT JOIN with IS NULL approach will definitely work for you
SELECT u.id
FROM users u
LEFT JOIN relationships r ON r.target_user_id = u.id
AND r.user_id = 1
WHERE u.is_active=1 AND r.target_user_id IS NULL
Nothing wrong with your query. MySQL should be able to use your index.
However, you can also use left join:
SELECT
users.id
FROM
users
LEFT JOIN relationships ON (
users.id = relationships.target_user_id
/*
-- un-comment if filtering by user is needed
AND relationships.user_id = 1
*/
)
WHERE
users.is_active=1
AND relationships.target_user_id IS NULL
-- addition filtering can be here
UPDATE:
If you filtering by user, you can try to add composite index (user_id, target_user_id) to relationships table (columns should be in this order)
I have a table in MySQL which looks like this.
+---------+------------+--------------+
| user_id | key | value |
+---------+------------+--------------+
| 1 | full_name | John Smith |
+---------+------------+--------------+
| 1 | is_active | 1 |
+---------+------------+--------------+
| 1 | user_level |Administrator |
+---------+------------+--------------+
I need to get value of key full_name where user_id is 1, but only if value of key is_active is 1. I can do it with 2 separate queries, but I would like to know if it is possible to do it in a single query.
Note: I cannot change the structure of the table.
One method is to use joins:
select tn.value
from t tn join
t ta
on tn.user_id = ta.user_id and ta.key = 'active'
where tn.key = 'fullname';
i think you need below query by using exists
select t.value from your_table t where
exists ( select 1 from your_table t1
where t1.user_id=t.user_id
and t1.key='is_active'
) and t.key='full_name'
DEMO IN MYSQL 8
value
john smith
CREATE TABLE test (
id INT(12),
time VARCHAR(16),
group INT(2),
taken TINYINT(1),
RID int(11) NOT NULL auto_increment,
primary KEY (RID));
id | time | group | taken
---------------------------
1 | 13.00| 1 | 1
---------------------------
2 | 13.00| 2 | 0
---------------------------
3 | 14.00| 2 | 0
---------------------------
4 | 15.00| 2 | 0
---------------------------
5 | 12.00| 3 | 0
Having a table structure and sample data as above, I want to get the smallest "group" number which has not been "taken" (taken=0)
I have come with two queries :
SELECT * From `test`
WHERE taken=0
and
SELECT * FROM `test`
WHERE `group` = ( SELECT MIN(`group`) FROM `test` )
Can someone show me how to combine the two queries so that I can pass the results of the first query to the second query to get as below.
id | time | group | taken
---------------------------
2 | 13.00| 2 | 0
---------------------------
3 | 14.00| 2 | 0
---------------------------
4 | 15.00| 2 | 0
---------------------------
You can use the result of the first query in the second query as follows:
SELECT *
FROM TEST
WHERE `group` = (SELECT MIN(`group`)
FROM `test`
WHERE taken = 0)
Which gives you the desired result according to this SQLFiddle
Use the sub query to get the lowest group for taken of 0. Join your main table to the results of the sub query.
Something like this:-
SELECT a.*
From `test` a
INNER JOIN
(
SELECT MIN(`group`) AS min_group
FROM `test`
WHERE taken=0
) b
ON a.taken = b.taken
AND a.`group` = b.min_group
try this:
SELECT min(`group`) FROM (
SELECT * FROM test
WHERE taken = 0)
AS t;
i have a mySQL table set up like this
+----+----------+---------+
| id | parentid | content |
+----+----------+---------+
| 1 | 0 | a |
| 2 | 1 | b |
| 3 | 0 | c |
| 4 | 3 | d |
| 5 | 3 | e |
| 6 | 3 | f |
+----+----------+---------+
what i would like to do is concatenate the content of the children onto the end of the parent (then delete the children, but i will do that later), in ASC order based on id. so the result should look like this (without children)
+----+----------+---------+
| id | parentid | content |
+----+----------+---------+
| 1 | 0 | ab |
| 3 | 0 | cdef |
+----+----------+---------+
the issue im running into is that as you can see a parent may have more than one child. so far the query i have is
UPDATE table
SET content = CONCAT(content,
...
) ORDER BY id ASC
im not sure what to place in the ... section to grab all of the children and append them in order they were retrieved. maybe im going about it the wrong way. any help will be greatly appreciated
One option is:
/*Table structure for table `table` */
DROP TABLE IF EXISTS `table`;
CREATE TABLE `table` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`parentid` INT(11) UNSIGNED NOT NULL,
`content` VARCHAR(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB;
/*Data for the table `table` */
INSERT INTO `table`(`parentid`,`content`)
VALUES
(0,'a'),(1,'b'),(0,'c'),(3,'d'),(3,'e'),(3,'f');
UPDATE `table`
INNER JOIN
(SELECT `t0`.`id`, CONCAT(`t0`.`content`, GROUP_CONCAT(`t1`.`content` SEPARATOR '')) AS `content`
FROM `table` `t0`
INNER JOIN `table` `t1` ON `t0`.`id` = `t1`.`parentid`
WHERE `t0`.`parentid` = 0
GROUP BY `t1`.`parentid`) `der`
SET `table`.`content` = `der`.`content`
WHERE `table`.`id` = `der`.`id`;
DELETE FROM `table` WHERE `parentid` > 0;
SQL Fiddle demo
You have to perfom so much changes to the table that it might be better to create another one with the new data and drop the current one.
The query that will get you the results you're looking for is:
SELECT
min(t1.id) id,
0 parentid,
group_concat(t1.content ORDER BY t1.id separator '') content
FROM t t1
LEFT JOIN t t2 ON t1.parentid = t2.id
GROUP BY coalesce(t2.id, t1.id);
Fiddle here.