mySQL WHERE clause with column = val1 OR coumn IN values - mysql

I am trying to query through my db to receive all posts which contain a certain user_id as owner or participant of the post.
My DB structure is as following:
id: INT
media_id: INT
owner_id: INT
participants: STRING eg. [1,2]
comments: TEXT eg. [{},{}]
Here are two example entries:
id: 1, media_id: 2, owner_id: 1, participants: "[1,2]", comments: "[]"
id: 2, media_id: 3, owner_id: 2, participants: "[2,1]", comments: "[]"
What I am trying to achieve is to get all rows where the user with the id 1 is part of the column owner_id OR participants
My current query looks as following:
SELECT * FROM posts WHERE owner_id = 1 OR participants IN "1"
And the result which I receive is only the line where 1 is the owner_id
I might understand the IN equasion wrong but 1 is a part of participants in both rows, therefore I should get both rows as result, shouldn't I?

This will do the trick:
SELECT * FROM posts WHERE owner_id = 1
OR participants like "%,1]"
OR participants like "[1,%"
OR participants like "%,1,%"

If you are willing to change your data structure for participants field to a comma separated value e.g. 1,2 or 2,1, you can then use:
SELECT * FROM posts WHERE owner_id = 1 OR FIND_IN_SET(1, participants);

You should change query to :
SELECT * FROM posts
WHERE (owner_id = 1 OR participants like "%,1" OR participants like "1,%" OR participants like "%,1,%")

You can use the KeyWord Like... the syntax example:
SELECT * FROM posts WHERE owner_id = 1 OR participants Like '%1%'
For other case you can use this:
SELECT * FROM posts WHERE owner_id = 1
OR participants like "%,1"
OR participants like "1,%"
OR participants like "%,1,%"

It is indeed not the correct approach of the IN clause. It should look like:
SELECT * FROM posts WHERE owner_id = 1 OR participants IN (1)
And its only need if looking for multiple values (in this case id's) like:
SELECT * FROM posts WHERE owner_id = 1 OR participants IN (1,5,15)
But because your participants column is a string it is not going to work, because MySQL is not parse/do something with the string to make it usable in the way you tried.
There is a possible and ugly way to achief what you want and do not change your database structure, but I do recommend to change it to a more usable approach with a relation table for a many-to-many relationship?
But the ugly way to achief it:
SELECT * FROM posts
WHERE owner_id = 1
OR (participants LIKE '[1,%' OR participants LIKE '%,1,%' OR participants LIKE '%,1]')
Better approach
A nice way good be to change your posts table slightly:
[posts]
id INT
media_id INT
owner_id INT
comments: TEXT eg. [{},{}]
And add a relational table what could like:
[post_participant]
post_id: INT
user_id: INT // -> or named participant_id
In this way you can get the proper posts with a query like:
SELECT p.* FROM posts p
LEFT JOIN post_participant pp ON pp.post_id = p.id AND pp.user_id = p.owner_id
WHERE p.owner_id = 1
OR pp.user_id IS NOT NULL

Related

SQL: Return immediately if one matched record found

I have one table with user and their posts. It looks like "user_id | post_id | post_status".
Now I have a list of userid (ex, 100 users) and I want to know how many of them has at least one post that gets deleted (ex, post_status 3).
Here is my sample search:
select count(distinct user_id)
from post_table
where user_id in ( {my set} )
and post_status=3
It runs super slow since it iterates the entire table. Is there a way to speed up the query?
Use something like
SELECT COUNT(*)
FROM
-- the list of userid as a rowset
( SELECT 123 AS user_id UNION ALL
SELECT 456 UNION ALL
-- ...
SELECT 789
) user_id_list
WHERE EXISTS ( SELECT NULL
FROM post_table
WHERE post_table.user_id = user_id_list.user_id
AND post_table.post_status = 3 )
If your MySQL version is 8.0.4 or above then you may provide the users list as CSV/JSON and parse it using JSON_TABLE (the query text will be more compact).
INDEX(post_status, user_id)
may help speed up your query, especially if very few rows have status=3.
This could also speed up Akina's solution.

Join 3 Tables Doesn't Return Correct Result

I have three tables and their structure are below.
In keyword table I have unique keywords. A keyword has more than one user or domain_id so I have keyword_user table.
In rank table I have some numbers related to keywords.
I want to return all keywords related with my selected domain_id.
My conditions are:
Must: return min(rank_position) from rank table
Must: rank.rank_date = keyword.keyword_last_date
Problem is:
If I write keyword_user.domain_id = 1234 it returns all rows from keyword table but rank results are incorrect.
If I write rank.domain_id it returns only rows that related with keyword_last_date normally.
using min(rank_position) on SELECT causing wrong results.
I want all keywords from keyword table where keyword_user.domain_id = XXX and rank_date = keyword.keyword_last_date. Domain must my domain id not anything else.
SELECT rank.rank_id
, keyword_name
, keyword_last_date
, MIN(rank_position) my_rank
, rank_url
, rank.domain_id
FROM keyword
LEFT
JOIN keyword_user
ON keyword.keyword_id = keyword_user.keyword_id
LEFT
JOIN rank
ON keyword.keyword_id = rank.keyword_id
WHERE keyword_user.domain_id = 8262
AND rank.domain_id = 8262
AND rank_date = keyword.keyword_last_date
GROUP
BY keyword.keyword_name
ORDER
BY rakip_rank
As you can see from picture. domain_id column has different values but I have domain_id = 8262. They are not related to my expected result. Also my_rank column should different values not only "1".
TABLE: keyword
TABLE: rank
TABLE: keyword_user

In SQL if where condition does not yield result fetch other rows

I have Query like this
Select * from customers where id = 123 and name like '%tester%';
If id : 123 and name : "tester" doesn’t exist in table i should fetch other rows with name "tester" discarding condition "id". if it exists fetch row for that id and name.
Guys i know this is can be handled in program, i want this to be done in my Query, can you please STOP DOWN VOTING and give me the solution if you know!!!
You can try something like this:
SELECT *
FROM CUSTOMERS
WHERE ( ID = 123
AND NAME LIKE '%tester%' )
OR ( NAME LIKE '%tester%'
AND NOT EXISTS (SELECT *
FROM CUSTOMERS
WHERE ID = 123
AND NAME LIKE '%tester%') )
You can find a working example on SQL Fiddle.
SELECT * FROM customers WHERE (id = 123 AND name like '%tester%') OR (name LIKE '%tester%') LIMIT 1;
It should work then
I gotcha here, check it
Select * from customers where id = 3 or name like '%tester%'
order by id=3 desc limit 1;
See you will get all rows that have either id= 3 or name is like tester. From there you will order then by the boolean value (1 if true) by if they == 3. Limiting this to 1 result will get you only the best response.
Likewise if you want to get all results you could remove the limit, assume the top one is the best result. If the first results id is not = 3 then you could say that all the results are just best matches.

MySQL: check if data exist inside query

I never used "if-else" or "case" inside a sql-query, but I guess I need to this time.
I have a table whose data represents something like a competition between two users:
//properties of "competition-table
int competitionId
int userId_Contrahent1
int userId_Contrahent2
otherdata....
Users can vote for one or the other contrahent within that competition; a vote is represented like so:
//properties of vote-table
int voteId
int competitionId
int userId_Voter
int userId_Winner // the user for which this vote counts
otherdata....
Obviously every given user in my Webapplication can only vote once for any given competition. So when I query for the competition I also want to have the information if the currentUser (which owns the current session) already voted for this competition. So in addition to all the other properties of competition I like to have an additional one, that has a key like "voted" and a value like "yes" or "no".
So my select statement should look something like this I guess:
SELECT competition.*,
If EXISTS (
SELECT * FROM vote WHERE userId_Voter = $currentUserId
AND competitionId = competition.competitionId)
...do something
FROM competition;
How do I do that exactly in MySQL?
SELECT c.*,
IF(v.competitionId IS NOT NULL, 'voted', 'not voted') AS `verdict`
FROM competition c
LEFT JOIN vote v ON v.competitionId = c.competitionId
AND v.userId_Voter = $currentUserId

How to find the next record after a specified one in SQL?

I'd like to use a single SQL query (in MySQL) to find the record which comes after one that I specify.
I.e., if the table has:
id, fruit
-- -----
1 apples
2 pears
3 oranges
I'd like to be able to do a query like:
SELECT * FROM table where previous_record has id=1 order by id;
(clearly that's not real SQL syntax, I'm just using pseudo-SQL to illustrate what I'm trying to achieve)
which would return:
2, pears
My current solution is just to fetch all the records, and look through them in PHP, but that's slower than I'd like. Is there a quicker way to do it?
I'd be happy with something that returned two rows -- i.e. the one with the specified value and the following row.
EDIT: Sorry, my question was badly worded. Unfortunately, my definition of "next" is not based on ID, but on alphabetical order of fruit name. Hence, my example above is wrong, and should return oranges, as it comes alphabetically next after apples. Is there a way to do the comparison on strings instead of ids?
After the question's edit and the simplification below, we can change it to
SELECT id FROM table WHERE fruit > 'apples' ORDER BY fruit LIMIT 1
SELECT * FROM table WHERE id > 1 ORDER BY id LIMIT 1
Even simpler
UPDATE:
SELECT * FROM table WHERE fruit > 'apples' ORDER BY fruit LIMIT 1
So simple, and no gymnastics required
Select * from Table
where id =
(Select Max(id) from Table
where id < #Id)
or, based on the string #fruitName = 'apples', or 'oranges' etc...
Select * from Table
where id =
(Select Max(id) from Table
where id < (Select id from Table
Where fruit = #fruitName))
I'm not familiar with the MySQL syntax, but with SQL Server you can do something with "top", for example:
SELECT TOP 1 * FROM table WHERE id > 1 ORDER BY id;
This assumes that the id field is unique. If it is not unique (say, a foreign key), you can do something similar and then join back against the same table.
Since I don't use MySQL, I am not sure of the syntax, but would imagine it to be similar.
Unless you specify a sort order, I don't believe the concepts of "previous" or "next" are available to you in SQL. You aren't guaranteed a particular order by the RDBMS by default. If you can sort by some column into ascending or descending order that's another matter.
This should work. The string 'apples' will need to be a parameter.
Fill in that parameter with a string, and this query will return the entire record for the first fruit after that item, in alphabetical order.
Unlike the LIMIT 1 approach, this should be platform-independent.
--STEP THREE: Get the full record w/the ID we found in step 2
select *
from
fruits fr
,(
--STEP TWO: Get the ID # of the name we found in step 1
select
min(vendor_id) min_id
from
fruits fr1
,(
--STEP ONE: Get the next name after "apples"
select min(name) next_name
from fruits frx
where frx.name > 'apples'
) minval
where fr1.name = minval.next_name
) x
where fr.vendor_id = x.min_id;
The equivalent to the LIMIT 1 approach in Oracle (just for reference) would be this:
select *
from
(
select *
from fruits frx
where frx.name > 'apples'
order by name
)
where rownum = 1
I don't know MySQL SQL but I still try
select n.id
from fruit n
, fruit p
where n.id = p.id + 1;
edit:
select n.id, n.fruitname
from fruits n
, fruits p
where n.id = p.id + 1;
edit two:
Jason Lepack has said that that doesn't work when there are gaps and that is true and I should read the question better.
I should have used analytics to sort the results on fruitname
select id
, fruitname
, lead(id) over (order by fruitname) id_next
, lead(fruitname) over (order by fruitname) fruitname_next
from fruits;
If you are using MS SQL Server 2008 (not sure if available for previous versions)...
In the event that you are trying to find the next record and you do not have a unique ID to reference in an applicable manner, try using ROW_NUMBER(). See this link
Depending on how savvy your T-SQL skill is, you can create row numbers based on your sorting order. Then you can find more than just the previous and next record. Utilize it in views or sub-queries to find another record relative to the current record's row number.
SELECT cur.id as id, nxt.id as nextId, prev.id as prevId FROM video as cur
LEFT JOIN video as nxt ON nxt.id > cur.id
LEFT JOIN video as prev ON prev.id < cur.id
WHERE cur.id = 12
ORDER BY prev.id DESC, nxt.id ASC
LIMIT 1
If you want the item with previous and next item this query lets you do just that.
This also allows You to have gaps in the data!
How about this:
Select * from table where id = 1 + 1