check for two values in a table - mysql

I have a simple table where I store some data related to a user.
id
id_user
id_func
1
1
1
2
1
2
3
2
1
I want to check if a specific user has two specific functions (1 AND 2) assigned. So If I check for user 1 the query will return true. For user 2 it will return false.
The query I have so far is:
SELECT IF(
"SELECT id_user FROM funz_abilitate WHERE id_func=1 AND id_func=2 AND id_user=1",
true,
false
) as verifica
FROM funz_abilitate
WHERE id_user=1
but this is returning for user 1:
verifica
0
0
and for user 2:
verifica
0
While I'd like just to get a boolean true/false. So if I select user 1 it returns true, if I select user 2 it returns false
How to fix it?

You can do it with aggregation:
SELECT COUNT(*) = 2 AS verifica
FROM funz_abilitate
WHERE id_user = 1 AND id_func IN (1, 2)
or:
SELECT COUNT(DISTINCT id_func) = 2 AS verifica
FROM funz_abilitate
WHERE id_user = 1 AND id_func IN (1, 2)
if there are duplicate id_funcs for each user.
In MySql 8.0+ I would use a CTE which returns the id_funcs I search for, so there is no need to write how many they are:
WITH cte(id_func) AS (VALUES ROW(1), ROW(2))
SELECT COUNT(*) = (SELECT COUNT(*) FROM cte) AS verifica
FROM funz_abilitate
WHERE id_user = 1 AND id_func IN (SELECT id_func FROM cte)
See the demo.

Related

How to reorganise the values of a groupBy from the same table (MYSQL)?

I have a Chats table setup like this:
ID
CHAT_GROUP_ID
MESSAGE
READ
CREATED_AT
1
uu-34uu5-6662
hi1
1
2022-06-02 13:16:42
2
uu-34uu5-6662
hi2
1
2022-06-02 13:16:45
3
uu-34uu5-6663
hi3
0
2022-06-02 13:16:46
4
uu-34uu5-6663
hi4
0
2022-06-02 13:16:47
5
uu-34uu5-6664
hi5
0
2022-06-02 13:16:49
ID = int
CHAT_GROUP_ID = Varchat(some kind of UUID)
MESSAGE = String
What I am trying to achieve is:
GROUP ALL THE CHAT_GROUP_ID with their own respective IDs.
When all the CHAT_GROUP_ID are grouped, SUM all the READ AS SUM(IF(read = 0, 1, 0))
Finally (and where I am struggling), always show only 1 MESSAGE but always the latest one.
I have am struggling so much on this unbelievable! How can I do this?
If you need it to be done in MySQL v5.*, you can use the following query:
SELECT tab.ID,
tab.CHAT_GROUP_ID,
tab.MESSAGE,
aggregated.SUM_READ_,
aggregated.MAX_TIME
FROM tab
INNER JOIN (SELECT CHAT_GROUP_ID,
SUM(IF(READ_=0,1,0)) AS SUM_READ_,
MAX(CREATED_AT) AS MAX_TIME
FROM tab
GROUP BY CHAT_GROUP_ID ) aggregated
ON tab.CHAT_GROUP_ID = aggregated.CHAT_GROUP_ID
AND tab.CREATED_AT = aggregated.MAX_TIME
First you create a view containing aggregated value of READ and CREATED_AT with respect to CHAT_GROUP_ID, then use these information to retrieve the needed infos from the main table.
Try it here.
Assuming at least MySql 8:
SELECT ct.Chat_Group_ID, ct.Message,
( SELECT SUM(Case when read = 1 then 0 else 1 end)
FROM Chats ct2
WHERE ct2.Chat_Group_ID = ct.Chat_Group_ID
) as Unread
FROM (
SELECT Chat_Group_ID, Message
, row_number() over (partition by Chat_Group_ID order by create_at desc) rn
FROM Chats
) ct
WHERE ct.rn = 1

SQL: Need to get aggregrated status by priority across related tables in MySQL

I have a Parent table A in MySQL DB 5.5.x with columns
Id(PK) Name Value
1 ABC 0.1
2 XYZ 0.2
3 PQR 0.3
And a related table B which reference Id from parent (FK)
status can have only 3 possible values i.e. pass,warn,error
with error check being higher priority than warn and so on
CheckName CheckStatus Id
L1 pass 1
L2 pass 1
L3 warn 1
L4 error 1
L1 pass 2
L2 warn 2
L3 pass 2
I want to create an SQL statement which can get aggregrate result from parent table A such that
if Id 1 has any errors reported against it I label the finalState for Id 1 as ERROR,
if no errors found check if Id 1 has any warnings reported against it and label the finalState as WARN
finally Mark it as pass.
I am unable to do a simple JOIN or add a case statement in the select claues
as i start getting multiple rows here.
Can I do this without using Stored Procedures ?.
Result Expected based on sample data in final select query is as follows:-
Id Name FinalStatus
1 ABC error
2 XYZ warn
Thanks!
EDIT:
Approach 1 (That I tried here):
select a.Id,
case when b.CheckStatus='error' then 'ERROR'
case when b.CheckStatus='warn' then 'WARN'
case when b.CheckStatus='pass' then 'PASS'
from a join b on
a.Id=b.Id
This is a prioritization query. I am only focusing on b -- you can bring in the columns from a using a simple join.
This works in MySQL 8+:
select b.*
from (select b.*,
row_number() over (partition by id
order by case checkstatus
when 'error' then 1
when 'warn' then 2
when 'pass' then 3
else 4
end
) as seqnum
from b
) b
where seqnum = 1;
In earlier versions, I would go for conditional aggregation:
select b.id,
max(finalstatus = 'error') as is_error,
max(finalstatus = 'warn') as is_warn,
max(finalstatus = 'pass') as is_pass
from b
group by b.id;
You can then get the final status as:
select b.id,
(case when max(checkstatus = 'error') > 0 then 'error'
when max(checkstatus = 'warn') > 0 then 'warn'
when max(checkstatus = 'pass') > 0 then 'pass'
end) as finalstatus
from b
group by b.id;

How to use an if condition in a query?

Suppose I want select the matches with the MAX gameweek if the status of all matches is setted to 3, or the matches with MIN gameweek how can I do this?
Data sample
id | status | gameweek | round_id
1 3 3 1
2 3 3 1
3 1 3 1
4 1 4 1
5 1 4 1
6 1 4 1
the final result should be: 1, 2, 3 because not all the match has played.
I was able to setup a query for MAX:
SELECT MAX(gameweek) FROM `match` m WHERE round_id = 1 AND m.status = 3
but I have some difficult to implement an IF condition, someone could help me?
Thanks.
UPDATE
In the solution proposed by the users I noticed that I doesn't explain well a particular situation: if all the matches of a round doesn't have the status 3, the query should return the MIN(gameweek) of the round_id specified.
If I get this right, you could use a CASE. If a record with a status other than 3 exists return the minumum gameweek, else the maximum. Compare the result to the gameweek.
SELECT `m1`.*
FROM `match` `m1`
WHERE `m1`.`round_id` = 1
AND `m1`.`gameweek` = CASE
WHEN EXISTS (SELECT *
FROM `match` `m2`
WHERE `m2`.`round_id` = `m1`.`round_id`
AND `m2`.`status` <> 3) THEN
(SELECT min(`m3`.`gameweek`)
FROM `match` `m3`
WHERE `m3`.`round_id` = `m1`.`round_id`)
ELSE
(SELECT max(`m4`.`gameweek`)
FROM `match` `m4`
WHERE `m4`.`round_id` = `m1`.`round_id`)
END;
I'm not sure if you wanted to limit it to a certain round_id. It's in your query but not in the text. If you don't want it, remove all the conditions related to round_id.
Edit:
To use the maximum gameweek if not all status of a round are equal to 3 and the minimum gameweek otherwise you can check if the minimum of the status equals the maximum (i.e. all status are the same) and if it is equal to 3.
SELECT `m1`.*
FROM `match` `m1`
WHERE `m1`.`round_id` = 1
AND `m1`.`gameweek` = CASE
WHEN EXISTS (SELECT min(`m2`.`status`)
FROM `match` `m2`
WHERE `m2`.`round_id` = `m1`.`round_id`
HAVING min(`m2`.`status`) <> max(`m2`.`status`)
OR min(`m2`.`status`) <> 3) THEN
(SELECT min(`m3`.`gameweek`)
FROM `match` `m3`
WHERE `m3`.`round_id` = `m1`.`round_id`)
ELSE
(SELECT max(`m4`.`gameweek`)
FROM `match` `m4`
WHERE `m4`.`round_id` = `m1`.`round_id`)
END;
Assuming there can be more than one round_id:
SELECT *
FROM `match` m
WHERE (gameweek, round_id ) =
(
SELECT MAX(gameweek),round_id
FROM `match` m
WHERE round_id = 1 AND m.status = 3
GROUP BY round_id
)
DBFiddle DEMO
ok, simpler than I imagined, I discovered that I can achieve this target using the COALESCE operator, so:
m.gameweek = (SELECT COALESCE(MIN(
CASE WHEN m2.status < 3
THEN m2.gameweek END),
MAX(m2.gameweek))
FROM `match` m2
WHERE m2.round_id = m.round_id)

Getting Follower and FollwedBy Users from Table in one list

I have a Table that tracks followers
FollowerUserId, FollowingUserId
1 2
2 1
3 1
4 1
1 5
I want to get all user that given Id follows and is followed by or Both.
for example for UserId 1,I want result to be: (FG: Following, FD: Followed, B: Both ways)
2,B
5,FG
3,FD
4,FD
i can easily get FG and FD by doing union
Select FollowerUserId, 'FD' From Table Where FollowingUserId =1
Union
Select FollowingUserId, 'FG' From Table Where FollowerUserId =1;
with above i get user 2 as
2,FG
2,FD
from above but I really need 2,B without UserId 2 duplicated.
How can this be done efficiently?
You can use aggregation on your basic query:
SELECT UserId,
(CASE WHEN COUNT(DISTINCT which) = 1 THEN MIN(which)
ELSE 'B'
END)
FROM (Select FollowerUserId as UserId, 'FD' as which From Table Where FollowingUserId = 1
Union ALL
Select FollowingUserId, 'FG' From Table Where FollowerUserId = 1
) f
GROUP BY UserId;

Complex SQL Query: Select like tree traversal

Consider the following (1:N) relationship:
[entity: user] <------ rid key ------> [entity: rid].
consider the data in both tables as:
select * from user;
user-id rid-key
a-basa a
b-basa b
a.a-basa a.a
a.b-basa a.b
a.a.a-basa a.a.a
a.a.b-basa a.a.b
a.b.a-basa a.b.a
a.b.b-basa a.b.b
a.b.b.a-basa a.b.b.a
a.b.b.b-basa a.b.b.b
select * from rid;
rid-key parent-rid enabled
a null true
b null true
a.a a true
a.b a false
a.a.a a.a true
a.b.a a.b true
a.b.b a.b true
a.b.b.a a.b.b true
......
n rows
I need to design a single query (not stored procedure) which will input a user-id, and the following facts are considered:
If an user is given access to a rid, then it can also access the parent rid of the rid given - the rid itself is enabled (enabled = true).
This should continue till we reach the root rid, ie. parent rid property is null.
In above example, the list of accessible rid for the user 'a.b.b.a-basa' will be:
a.b.b.a
a.b.b
a.b
and for a.a.a-basa:
a.a.a
a.a
a
can we get this list using a single query? Any sql vendor is fine.
There are several models for Hierarchical data. Most models (like the Adjacency List) require some sort of recursion for some queries. With your design that uses the Materialized Path model, what you want is possible without a recursive query.
Tested in MySQL (that has no recursive queries), at SQL-fiddle test-mysql. It can be easily converted for other DBMS, if you modify the string concatenation part:
SELECT
COUNT(*)-1 AS steps_up,
rid2.rid_key AS ancestor_rid_key
FROM
u2
JOIN
rid
ON u2.rid_key = rid.rid_key
OR u2.rid_key LIKE CONCAT(rid.rid_key, '.%')
JOIN
rid AS rid2
ON rid.rid_key = rid2.rid_key
OR rid.rid_key LIKE CONCAT(rid2.rid_key, '.%')
WHERE
u2.userid = 'basa'
AND
u2.rid_key = 'a.b.b.a'
GROUP BY
rid2.rid_key, rid2.enabled
HAVING
COUNT(*) + (rid2.enabled = 'true')
= SUM(rid.enabled = 'true') + 1 ;
It uses this view, which is not strictly needed but it shows that the user.user_id is storing data that you already have in the rid_key column.
CREATE VIEW u2 AS
SELECT
SUBSTRING_INDEX(user_id, '-', -1) AS userid
, rid_key
FROM user ;
One more note is that the above query does not use the parent_rid column at all. And that I'm sure it can be further improved.
In Oracle, you can achieve this using a hierarhical query. Search for CONNECT BY or have a look at this article.
This should get the ball rolling for you.
The answer works on SQL Server 2005 onwards
DECLARE #UsersRIDkey VARCHAR(10) = 'a.a.a'
;WITH UserCTE (userid, ridkey) AS
(
SELECT 'a-basa', 'a' UNION ALL
SELECT 'b-basa', 'b' UNION ALL
SELECT 'a.a-basa', 'a.a' UNION ALL
SELECT 'a.b-basa', 'a.b' UNION ALL
SELECT 'a.a.a-basa', 'a.a.a' UNION ALL
SELECT 'a.a.b-basa', 'a.a.b' UNION ALL
SELECT 'a.b.a-basa', 'a.b.a' UNION ALL
SELECT 'a.b.b-basa', 'a.b.b' UNION ALL
SELECT 'a.b.b.a-basa', 'a.b.b.a' UNION ALL
SELECT 'a.b.b.b-basa', 'a.b.b.b'
)
,RidCTE (ridkey, parentrid, isenabled) AS
(
SELECT 'a', null, 1 UNION ALL
SELECT 'b', null, 1 UNION ALL
SELECT 'a.a', 'a', 1 UNION ALL
SELECT 'a.b', 'a', 0 UNION ALL
SELECT 'a.a.a', 'a.a', 1 UNION ALL
SELECT 'a.b.a', 'a.b', 1 UNION ALL
SELECT 'a.b.b', 'a.b', 1 UNION ALL
SELECT 'a.b.b.a', 'a.b.b', 1
)
,RidHierarchyCTE AS
(
SELECT *
FROM RidCTE
WHERE ridkey = #UsersRIDkey
UNION ALL
SELECT R.ridkey, R.parentrid, R.isenabled
FROM RidHierarchyCTE H
JOIN RidCTE R ON R.ridkey = H.parentrid
)
SELECT ridkey
FROM RidHierarchyCTE
Oracle solution:
SQL> select u.user_id, r.rid_key, r.parent_rid, r.enabled
2 from users u
3 inner join rid r
4 on r.rid_key = u.rid_key
5 start with u.user_id = 'a.a.a-basa'
6 connect by prior r.parent_rid = r.rid_key and prior enabled = 'true'
7 /
USER_ID RID_KEY PAREN ENABL
------------ ------- ----- -----
a.a.a-basa a.a.a a.a true
a.a-basa a.a a true
a-basa a null true
SQL> select u.user_id, r.rid_key, r.parent_rid, r.enabled
2 from users u
3 inner join rid r
4 on r.rid_key = u.rid_key
5 start with u.user_id = 'a.b.b.a-basa'
6 connect by prior r.parent_rid = r.rid_key and prior enabled = 'true'
7 /
USER_ID RID_KEY PAREN ENABL
------------ ------- ----- -----
a.b.b.a-basa a.b.b.a a.b.b true
a.b.b-basa a.b.b a.b true
a.b-basa a.b a false
http://sqlfiddle.com/#!4/d529f/1