Not sure if this is possible but I have a schema like this:
id | user_id | thread_id
1 | 1 | 1
2 | 4 | 1
3 | 1 | 2
4 | 3 | 2
I am trying to retrieve the thread_id where user_id = 1 and 4. I know that in(1,4) does not fit my needs as its pretty much a OR and will pull up record 3 as well and Exists only returns a bool.
You may use JOIN (that answer already exists) or HAVING, like this:
SELECT
thread_id,
COUNT(1) AS user_count
FROM
t
WHERE
user_id IN (1,4)
GROUP BY
thread_id
HAVING
user_count=2
-check the demo. HAVING will fit better in case of many id's (because with JOIN you'll need to join as many times as many id you have). This is a bit tricky, however: you may do = comparison only if your records are unique per (user_id, thread_id); for example, your user_id can repeat, then use >=, like in this demo.
Try this with join, i guess you need to do AND operation with user_id must be 4 and 1 then
SELECT
t1.thread_id
FROM
TABLE t1
JOIN TABLE t2
ON (t1.user_id = t2.user_id)
WHERE t1.user_id = 1
AND t2.user_id = 4
Related
I am making a web dating app that needs to match users and let them chat with each other.
I want to figure out how to find all the matches for a particular user.
Right now I have a table called follows that has 2 columns.
UserID | MatchUserID
--------------------
1 | 2
2 | 1
1 | 3
1 | 4
1 | 5
4 | 1
5 | 4
The idea is that for two users to match they need to follow one another. The table above shows which user follows which.
Assuming that the user who is currently logged on is UserID = 1.
I need a query that will return from the MatchUserID table the following results:
2, 4
In a way, I am looking to find all the opposite combinations between the two columns.
This is the code I use to create the table.
CREATE TABLE Match
(
UserID INT NOT NULL,
MatchUserID INT NOT NULL,
PRIMARY KEY (UserID, MatchUserID)
);
You can do it with a self join:
select m.MatchUserID
from `Match` m inner join `Match` mm
on mm.MatchUserID = m.UserId
where
m.UserId = 1
and
m.MatchUserID = mm.UserId
See the demo.
Results:
| MatchUserID |
| ----------- |
| 2 |
| 4 |
The simplest way possibly is to use EXISTS and a correlated subquery that searches for the other match.
SELECT t1.matchuserid
FROM elbat t1
WHERE t1.userid = 1
AND EXISTS (SELECT *
FROM elbat t2
WHERE t2.matchuserid = t1.userid
AND t2.userid = t1.matchuserid);
I need to implement a function which returns all the networks the installation is not part of.
Following is my table and for example if my installation id is 1 and I need all the network ids where the installation is not part of then the result will be only [9].
network_id | installation_id
-------------------------------
1 | 1
3 | 1
2 | 1
2 | 2
9 | 2
2 | 3
I know this could be solved with a join query but I'm not sure how to implement it for the same table. This is what I've tried so far.
select * from network_installations where installation_id = 1;
network_id | installation_id
-------------------------------
1 | 1
2 | 1
3 | 1
select * from network_installations where installation_id != 1;
network_id | installation_id
-------------------------------
9 | 2
2 | 2
2 | 3
The intersection of the two tables will result the expected answer, i.e. [9]. But though we have union, intersect is not present in mysql. A solution to find the intersection of the above two queries or a tip to implement it with a single query using join will be much appreciated.
The best way to do this is to use a network table (which I presume exists):
select n.*
from network n
where not exists (select 1
from network_installation ni
where ni.network_id = n.network_id and
ni.installation_id = 1
);
If, somehow, you don't have a network table, you can replace the from clause with:
from (select distinct network_id from network_installation) n
EDIT:
You can do this in a single query with no subqueries, but a join is superfluous. Just use group by:
select ni.network_id
from network_installation ni
group by ni.network_id
having sum(ni.installation_id = 1) = 0;
The having clause counts the number of matches for the given installation for each network id. The = 0 is saying that there are none.
Another solution using OUTER JOIN:
SELECT t1.network_id, t1.installation_id, t2.network_id, t2.installation_id
FROM tab t1 LEFT JOIN tab t2
ON t1.network_id = t2.network_id AND t2.installation_id = 1
WHERE t2.network_id IS NULL
You can check at http://www.sqlfiddle.com/#!9/4798d/2
select *
from network_installations
where network_id in
(select network_id
from network_installations
where installation_id = 1
group by network_id )
The solution to the topic is evading me.
I have a table looking like (beyond other fields that have nothing to do with my question):
NAME,CARDNUMBER,MEMBERTYPE
Now, I want a view that shows rows where the cardnumber AND membertype is identical. Both of these fields are integers. Name is VARCHAR. Name is not unique, and duplicate cardnumber, membertype should show for the same name, as well.
I.e. if the following was the table:
JOHN | 324 | 2
PETER | 642 | 1
MARK | 324 | 2
DIANNA | 753 | 2
SPIDERMAN | 642 | 1
JAMIE FOXX | 235 | 6
I would want:
JOHN | 324 | 2
MARK | 324 | 2
PETER | 642 | 1
SPIDERMAN | 642 | 1
this could just be sorted by cardnumber to make it useful to humans.
What's the most efficient way of doing this?
What's the most efficient way of doing this?
I believe a JOIN will be more efficient than EXISTS
SELECT t1.* FROM myTable t1
JOIN (
SELECT cardnumber, membertype
FROM myTable
GROUP BY cardnumber, membertype
HAVING COUNT(*) > 1
) t2 ON t1.cardnumber = t2.cardnumber AND t1.membertype = t2.membertype
Query plan: http://www.sqlfiddle.com/#!2/0abe3/1
You can use exists for this:
select *
from yourtable y
where exists (
select 1
from yourtable y2
where y.name <> y2.name
and y.cardnumber = y2.cardnumber
and y.membertype = y2.membertype)
SQL Fiddle Demo
Since you mentioned names can be duplicated, and that a duplicate name still means is a different person and should show up in the result set, we need to use a GROUP BY HAVING COUNT(*) > 1 in order to truly detect dupes. Then join this back to the main table to get your full result list.
Also since from your comments, it sounds like you are wrapping this into a view, you'll need to separate out the subquery.
CREATE VIEW DUP_CARDS
AS
SELECT CARDNUMBER, MEMBERTYPE
FROM mytable t2
GROUP BY CARDNUMBER, MEMBERTYPE
HAVING COUNT(*) > 1
CREATE VIEW DUP_ROWS
AS
SELECT t1.*
FROM mytable AS t1
INNER JOIN DUP_CARDS AS DUP
ON (T1.CARDNUMBER = DUP.CARDNUMBER AND T1.MEMBERTYPE = DUP.MEMBERTYPE )
SQL Fiddle Example
If you just need to know the valuepairs of the 3 fields that are not unique then you could simply do:
SELECT concat(NAME, "|", CARDNUMBER, "|", MEMBERTYPE) AS myIdentifier,
COUNT(*) AS count
FROM myTable
GROUP BY myIdentifier
HAVING count > 1
This will give you all the different pairs of NAME, CARDNUMBER and MEMBERTYPE that are used more than once with a count (how many times they are duplicated). This doesnt give you back the entries, you would have to do that in a second step.
Ok, I have a complicated query to get all article details, each article has many versions. I have a need to get details of article with latest version only (ie take the max version). Here is the table:
+------------+-----------+----------+
| ArticleID | Detail | Version |
+------------+-----------+----------+
| 1 | detail1 | 1 |
| 1 | detail2 | 1 |
| 1 | detail3 | 2 |
| 1 | detail4 | 2 |
| 3 | detail3 | 2 |
| 3 | detail6 | 2 |
| 3 | detail4 | 3 |
+------------+-----------+----------+
Now user just provides a detail & the query will take all details of all articles with version=max(version)
Suppose that if we don't care about max version, then a simple query could be
Select * from articleTb where Detail like '%3'
It will print out:
+------------+-----------+----------+
| ArticleID | Detail | Version |
+------------+-----------+----------+
| 1 | detail3 | 2 |
| 3 | detail3 | 2 |
+------------+-----------+----------+
But this doesn't meet the requirement cos the result should not have this record 3 - detail3 - 2 cos it doesn't contain the max version of articleID=3.
Let say user search for Detail like '%4', then a correct query should be:
ArticleID - Detail - Version
+----+-----------+----+
| 1 | detail4 | 2 |
+----+-----------+----+
| 3 | detail4 | 3 |
+----+-----------+----+
The 2 records appear cos they belongs to the article with max version. Explain, 2 is the maxversion of articleID=1 so it matches the condition, & 3 is the max version of articleID=3 so it also matches the condition.
So here is what i did,
select * from (Select * from articleTb where Detail like '%3') tb1
Join (select articleID, max(version) maxversion from articleTb where
Detail like '%3' group by articleID) tb2
on tb1.articleID=tb2.articleID and tb1.version=tb2.maxversion
However, for the above query the system have to duplicate the task where Detail like '%3' which is not good. Besides, my real world query1 is much more complicated than where Detail like '%3', then if i do like the above then the query will implement the same job TWICE? & that is very inefficient.
So how to deal this problem?
To improve performance, remove the unnecessary inline view, e.g.
SELECT tb1.*
FROM articleTb tb1
JOIN ( SELECT b.articleID
, MAX(b.version) AS maxversion
FROM articleTb b
WHERE b.Detail LIKE '%3'
GROUP BY b.articleID
) tb2
ON tb1.articleID = tb2.articleID
AND tb1.version = tb2.maxversion
WHERE tb1.Detail LIKE '%3'
and...
make sure you have appropriate indexes. A covering index with a leading column of article may enable MySQL to use the index to optimize the GROUP BY (avoiding a "Using filesort" operation.)
... ON articleTb (articleID, version, detail)
MySQL may also be able to use that index for the join to tb1; the derived table (inline view) won't have an index.
You can confirm the execution plan with an EXPLAIN.
I would use a CTE to create a table that contains the article id and the version id, then use that in my main query to filter down to the most recent version.
with latest as
(
select articleId, max(version) as version from articleTb
)
select ....
from articleTb a
inner join latestl on a.articleid = l.articleid and l.version = a.version
Use of aggregate table will helpful.
Let me describe a scenario first. Day 1, you get a flat file first time ever.
1. Load that in a staging table.
2. Find ArticleID, MAx (Version) for each Article ID, and store in the aggregate table.
3. Left outer join the stage table with the aggregate table joining on article ID. Pick the higher version. This will lead to your result.
4. Truncate the staging table.
Next day when a new feed arrives, the file will again be loaded into the truncated table, and left joined.
You can add a few audit fields in aggregate table such as date when that file arrived, maybe file name too. I had used this method in one of the projects in a insurance companies that resulted into several fold performance gain.
This is your query:
select *
from (Select * from articleTb where Detail like '%3'
) tb1 Join
(select articleID, max(version) maxversion
from articleTb
where Detail like '%3'
group by articleID
) tb2
on tb1.articleID=tb2.articleID and tb1.version=tb2.maxversion;
You are trying to get the last version of a particular type of article. Another approach is to use not exists:
select *
from articleTb t
where Detail like '%3' and
not exists (select 1
from articleTb t2
where t2.articleID = t1.articleID and
t2.Detail like '%3'
t2.version > t.version
);
This is saying: "Get me all the rows from articleTb where Detail ends in 3 and there isn't another version that is higher".
To improve performance, create an index on: articleTb(articleID, Detail, version). The one question is whether t2.Detail like '%3' is needed for the subquery -- does that condition filter articles or versions within an article? If it is not needed, then remove the index and change the condition to articleTb(articleID, version).
I have a join table named languages_services that basically joins the table services and languages.
I need to find a service that is able to serve both ENGLISH (language_id=1) and ESPANOL (language_id=2).
table languages_services
------------------------
service_id | language_id
------------------------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 3
With the data provided above, I want to test for language_id=1 AND language_id=2 where the result would look like this
QUERY RESULT
------------
service_id
------------
1
Obviously it doesn't return the one with service_id=2 because it doesn't service Espanol.
Any tips on this is greatly appreciated!
SELECT
service_id
FROM
language_services
WHERE
language_id = 1
OR language_id = 2
GROUP BY
service_id
HAVING
COUNT(*) = 2
Or...
WHERE
lanaguage_id IN (1,2)
GROUP BY
service_id
HAVING
COUNT(*) = 2
If you're always looking at 2 languages you could do it with joins, but the aggregate version is easier to adapt to differing numbers of language_ids. (Add an OR, or add an item to the IN list, and change the COUNT(*) = 2 to COUNT(*) = 3, etc, etc).
Be aware, however, that this scales very poorly. And with this table structure there isn't much you can do about that.
EDIT Example using a join for 2 languages
SELECT
lang1.service_id
FROM
language_services AS lang1
INNER JOIN
language_services AS lang2
ON lang1.service_id = lang2.service_id
WHERE
lang1.language_id = 1
AND lang2.language_id = 2