Filtering data in tables - mysql

I have two tables account_agent and bulk_index. The data on each table is as follows
account_agent
id name
1 Tom
2 Brad
3 John
4 Jan
5 Bartosz
account_agent to filter
id name
1 Tom
3 John
5 Bartosz
using these tables I want to make a normalized view that contains all agents from first table, but without agents placed in second table.
account_agent_filtered
name code
2 Brad
4 Jan
I have a lot of data and with cross join I could just do an opposite thing, but now I do not want to match. I want to filter and I do not have any idea how to do this.

Unfortunately, MySQL doesn't have an except set operator, but you could emulate this behavior using exists:
SELECT *
FROM account_agent aa
WHERE NOT EXISTS (SELECT *
FROM account_agent_to_filter aatf
WHERE aa.id = aatf.id AND aa.name = aatf.name)

In addition to Mureinik's answer:
SELECT t1.*
FROM account_agent t1
LEFT JOIN account_agent_to_filter t2 USING (id, name)
WHERE t2.id IS NULL

Related

mySQL Subselect with multiple results should be shown in 1 field

I want to have shown multiple results in 1 fields for a subselect.
As example:
table 1:
tbl1_ID, fistname, familyname
table 2:
tbl2_ID, carbrand
table 3 is the n:n relationship for table 1 and 2
tbl1, tbl2
The Person of table 1 should be able to own several cars (for example Ford and BMW).
The car brand of table 2 is applicable to several People of course.
I want to have listed the cars of each Person in 1 data field
Example:
Mueller | Hans | Ford,BMW
Jaeger | Erwin | BMW,Mercedes,Jaguar
Fritsche | Sascha | Mercedes
How to do this? I cannot do with subselect because it allows only 1 result.
Also, it doesn't work with LEFT JOIN because I want to have shown each Person once only.
Thanks! MR
You could use group_concat and you should use inner join between the two related tables based on table 3 and group by
select a.familyname, a.fistname, group_concat(b.carbrand)
from table_3 c
inner join table1 a on c.table1_id = a.table1_id
inner join table2 b on c.table2_id = b.table2_id
group by a.familyname, a.fistname

MySQL, How to select one row from first table and two from the second one

I need to SELECT one row from first table and two rows from second one that correspond to the result from the first one. I can explain it more clearly using example.
As shown below I have two tables. Table "Games" have information about games between teams (e.g. game nr 1 is played by team NR 10 and team NR 11). In second table I simply have teams' names. The question is how to using one Select get information about game together with teams names from second table?
e.g. I want to get information who plays game NR 3 together with teams' names. Result should be:
id = 3, team_nr1 = 14, team_nr2 = 15, team1_name = eee, team2_name = fff
Is it even possible?
Table: Games
________________________
id team_nr1 team_nr2
1 10 11
2 12 13
3 14 15
Table: Teams
________________________
team_id team_name
10 aaa
11 bbb
12 ccc
13 ddd
14 eee
15 fff
Thanks for any advice.
I think you want to use joins. It could look like this:
SELECT G.id, G.team_nr1, G.team_nr2,
T1.team_name AS team1_name, T2.team_name AS team2_name
FROM Games G
LEFT JOIN Teams T1 ON (G.team_nr1 = T1.team_id)
LEFT JOIN Teams T2 ON (G.team_nr2 = T2.team_id)
WHERE G.id = 3
There are also other types of joins. LEFT JOIN means that all games are listed, even if there are no matching teams in T1 or T2.
You could try:-
SELECT ID,
TEAM_NR1,
TEAN_NR2,
(SELECT TEAM_NAME
FROM TEAMS
WHERE TEAMS.TEAM_ID = GAMES.TEAM_NR1) AS TEAM1_NAME,
(SELECT TEAM_NAME
FROM TEAMS
WHERE TEAMS.TEAM_ID = GAMES.TEAM_NR2) AS TEAM2_NAME
FROM GAMES
WHERE GAMES.ID = 3;
The subqueries pick out the required team names and name the "column" in which they appear.
If you want to have only 1 row with all your values, the only way I can thing of is using the GROUP_CONCAT function. I would not recommend the use on inline queries oe subqueries because in case of no record found it will produce a mysql error.

Select records without any related records of some types

Basically I have two table
articleID
1
2
3
4
relatedType | articleID
3 1
4 1
3 2
4 3
5 3
2 4
I need to select the articleID that doesn't have any related records with type > 3. With this dataset I basically need:
articleID
2
4
Because their related type contain only 3 and 2.
I con do it with this query:
SELECT * FROM article
WHERE articleID NOT IN (SELECT articleID FROM relatedTable
^ WHERE type > 3 GROUP BY portalid )
|
|--- NOT IN does the trick!
BUT I would like to avoid nested query because this query is pretty slow. Any hint?
You can do
SELECT * FROM article a
WHERE NOT EXISTS
(SELECT NULL FROM relatedTable b WHERE b.type > 3
AND b.articleID = a.articleID)
Technically, all 3 ways to achieve the desired results (NOT IN, NOT EXISTS, LEFT JOIN) should behave the same (for non-nullable column) and normally generate the same execution plan except mysql where NOT IN is not recommended (or wasn't recommended prior to 5.5, maybe it changed).
I'd also blame GROUP BY in your original query for poor performance as well...
Use OUTER JOIN
SELECT a.articleID
FROM article a LEFT OUTER JOIN relatedTable r
ON (a.articleID = r.articleID and r.relatedType > 3)
WHERE r.articleID IS NULL
CORRECTION:
Sorry, I just realized that the request was not to have those rows listed which has ANY records with type > 3. You can still do it by having a sub-query in the JOIN or by creating a temp table, indexing it and then joining that. Whether any of these are actually faster than the NOT IN sub-query will depend on MySQL version and more importantly table size and stats.
If you need only article id, try this:
SELECT
articleID
FROM relatedTable
GROUP BY articleID
HAVING MAX(relatedType) <= 3
or you can JOIN this to your article table.

Mysql joining two tables

I need to join the following two tables
table_A
id userId name score game
1 2343 me 45 Palo Alto
2 6575 other 21 SF
3 6575 other 2 miami
table_B
id userId pen mango
1 2343 3 4
2 2343 5 7
3 6575 1 2
Here is the join:
SELECT COUNT(a.userId), SUM(b.pen), SUM(b.mango)
FROM table_A AS a
LEFT JOIN table_B b ON a.userId = b.userId
WHERE userId = 2343;
The problem is I am getting count(userId) equals to 2, but I need it to be 1. What am I doing wrong?
Change it to the following:
count(distinct a.userId)
Your query will gerneate two rows, each row corresponding to one row in table_B (one for id 1, one for id 2).
I am not sure why you think the resulting count(a.userId) should be 1, but you could enforce this by using a GROUP BY clause à la GROUP BY b.userId.
I am confused about what you are doing. You can still get SUM of Pen and Mango without joining the two tables. And another thing, why do you still use the COUNT function where, in fact, you know that you are querying to ONLY ONE ID? Right?
SELECT SUM(Pen) as TotalPen,
SUM(Mango) as TotalMango
FROM table_B
WHERE userId = 2343
But if you want a joined tables you could write something like this:
SELECT SUM(COALESCE(b.pen,0)) as TotalPen,
SUM(COALESCE(b.mango,0)) as TotalMango
FROM table_A AS a LEFT JOIN table_B b ON a.userId = b.userId
WHERE a.userId = 2343;
The problem is I am getting count(userId) equals to 2, but I need it to be 1. - The query is correct but your understanding is wrong. Obviously there are two record IDs of 2343 in Table_B

MySQL many-to-many complement set

I've been looking all over the net and asking people for guidance but nobody seems to know the right (relatively fast) solution to the problem:
I have three tables, classic many-to-many solution:
entries: id (int), title (varchar[255]), content (text)
tags: id (int), name (varchar[255]), slug (varchar[255])
entries_tags: id (int), entry_id (int), tag_id (int)
Nothing out of ordinary so far. Now let's say I have test data in tags (I'm keeping out slugs as they are not important):
ID | name
1. | one
2. | two
3. | three
4. | four
5. | five
I also have three entries:
ID | title
1. | Something
2. | Blah blah blah
3. | Yay!
And relations:
ID | entry_id | tag_id
1. | 1 | 1
2. | 1 | 2
3. | 2 | 1
4. | 2 | 3
5. | 3 | 1
6. | 3 | 2
7. | 3 | 3
8. | 4 | 1
9. | 4 | 4
OK, we have our test data. I want to know how to get all entries that have tag One, but doesn't have tag Three (that'd be entries 1 and 4).
I know how to do it with subquery, the problem is, it takes a lot of time (with 100k entries it took about 10-15 seconds). Is there any way to do it with JOINs? Or am I missing something?
edit I guess I should've mentioned I need a solution that works with sets of data rather than single tags, so replace 'One' in my question with 'One', 'Two' and 'Two' with 'Three','Four'
edit2 The answer provided is right, but it's too slow to be used practically. I guess the only way of making it work is using a 3rd-party search engine like Lucene or ElasticSearch.
The following script selects entries that have tags One and Two and do not have tags Three and Four:
SELECT DISTINCT
et.entry_id
FROM entries_tags et
INNER JOIN tags t1 ON et.tag_id = t1.id AND t1.name IN ('One', 'Two')
LEFT JOIN tags t2 ON et.tag_id = t2.id AND t2.name IN ('Three', 'Four')
WHERE t2.id IS NULL
Alternative solution: the INNER JOIN is replaced with WHERE EXISTS, which allows us to get rid from the (rather expensive) DISTINCT:
SELECT
et.entry_id
FROM entries_tags et
LEFT JOIN tags t2 ON et.tag_id = t2.id AND t2.name IN ('Three', 'Four')
WHERE t2.id IS NULL
AND EXISTS (
SELECT *
FROM tags t1
WHERE t1.id = et.tag_id
AND t1.name IN ('One', 'Two')
)
This should do what you want.
(It may or may not be faster than the sub query solution, I suggest you compare the query plans)
SELECT DISTINCT e.*
FROM tags t1
INNER JOIN entries_tags et1 ON t1.id=et1.tag_id
INNER JOIN entries e ON e.entry_id=et1.entry_id
INNER JOIN tags t2 on t2.name='three'
INNER JOIN tags t3 on t3.name='four'
LEFT JOIN entries_tags et2 ON (et1.entryid=et2.entryid AND t2.id = et2.tag_id )
OR (et1.entryid=et2.entryid AND t3.id = et2.tag_id )
WHERE t1.name IN ('one','two') AND et2.name is NULL
By LEFT Joining the entries_tags table et2 (the data you do not want), you can then only select the records where the et2.name IS NULL (where the et2 record does not exist).
You mentioned trying a subquery. Is this what you tried?
SELECT entries.id, entries.content
FROM entries
LEFT JOIN entries_tags ON entries.id=entries_tags.entries_id
LEFT JOIN tags ON entries_tags.tag_id=tags.id
WHERE tag.id=XX
and entries.id NOT IN (
SELECT entries.id
FROM entries
LEFT JOIN entries_tags ON entries.id=entries_tags.entries_id
LEFT JOIN tags ON entries_tags.tag_id=tags.id
WHERE tag.id=YY
)
(Where XX is the tag you do want and YY is the tag you do not want)
With indices on the ID fields, that shouldn't be as slow as you say it is. It will depend on the data set, but it should be fine with indices (and with string comparisons omitted).