MySQL: Grouping Duplicate ID with IF Condition - mysql

I have a table tblRater (which conatins all the users who can rate a topic) whose sample data is as follows:
RaterID | Name |
=================
rater_1 |Katty |
rater_2 |Batty |
rater_3 |Ratty |
rater_4 |Shatty |
rater_5 |Patty |
there is another table tblAuthorizedRater (which contains the data about which rater is assigned to rate which topic) whose sample data is as follows:
TopicID | RaterID |
===================
topic_1 | rater_1 |
topic_1 | rater_2 |
topic_1 | rater_3 |
topic_2 | rater_2 |
topic_2 | rater_3 |
topic_3 | rater_1 |
topic_3 | rater_2 |
topic_6 | rater_1 |
So when I look up for the list of raters who are allowed to rate topic_1, I expect the following data:
RaterID | Authorized|
=====================
rater_1 | 1 |
rater_2 | 1 |
rater_3 | 1 |
rater_4 | 0 |
rater_5 | 0 |
The above table is basically list of all the raters from tblRater and the list of authorized raters for topic_1 from tblAuthorizedRater.
I tried doing the left join with tblRater on left and tblAuthorizedRater on right and an if condition. However the results I am getting repeats the RaterID and if I do a group by on it, I am not getting the expected result.
Just for reference, this is the query I am trying so far:
select
tr.RaterID,
IF(tar.TopicID = 'topic_1', 1, 0) as Authorized
from tblRater as tr
left join tblAuthorizedRater as tar
on tr.RaterID = tar.RaterID;
I also tried looking up at subselect, union, but none of it helped.

Put topicID = 'topic_1' in the ON clause, so you only select those rows from tblAuthorizedRater. Then check whether the join found a matching row by testing for NULL.
SELECT
tr.RaterID,
tar.topicID IS NOT NULL AS Authorized
FROM tblRater AS tr
LEFT JOIN tblAuthorizedRater AS tar
ON tr.RaterID = tar.RaterID AND tar.TopicID = 'topic_1'
DEMO

Related

MySQL query to return a value if valus in two different rows match a condition

In the example below, I'm trying to create a query that returns the content of NAME if this contains both 'ammonium nitrate' and 'urea'. The thing is, they are in separate rows and I haven't found how to check for both. I've tried using the clauses IN, AND, and UNION, but to no avail.
The query should return only 'Gro-Fast', as it is the only NAME that contains both.
+-------+------------------+-------------+---------+
| FCODE | CNAME | NAME | CONTACT |
+-------+------------------+-------------+---------+
| 28994 | ammonium nitrate | Gro-Fast | 556698 |
| 28994 | urea | Gro-Fast | 556698 |
| 29462 | ammonium nitrate | BetterRoots | 342554 |
| 34588 | ammonium nitrate | Flourisher | 342554 |
| 83732 | urea | GreenAgain | 354211 |
+-------+------------------+-------------+---------+
TIA!
You can group by name and set the conditions in the HAVING clause:
select name
from tablename
where cname in ('ammonium nitrate', 'urea')
group by name
having count(distinct cname) = 2
If you want the column cnameto contain only 'ammonium nitrate' and 'urea':
select name
from tablename
group by name
having
sum(cname not in ('ammonium nitrate', 'urea')) = 0
and
count(distinct cname) = 2
See the demo.
Results:
> | name |
> | :------- |
> | Gro-Fast |
Edit, for your query:
select name
from CONTENTS natural join FERTILIZERS
where cname in ('ammonium nitrate', 'urea')
group by name
having count(distinct cname) = 2

Select value from table sorted by a certain order from another table

I want to select value from table sorted by a certain order.
I have a table called test that looks like this:
| date | code | value |
+----------+-----------+----------+
| 20050104 | 000005.SZ | -6359.19 |
| 20050104 | 600601.SH | -7876.34 |
| 20050104 | 600602.SH | -25693.3 |
| 20050104 | 600651.SH | NULL |
| 20050104 | 600652.SH | -15309.9 |
...
| 20050105 | 000005.SZ | -4276.28 |
| 20050105 | 600601.SH | -3214.56 |
...
| 20170405 | 000005.SZ | 23978.13 |
| 20170405 | 600601.SH | 32212.54 |
Right now I want to select only one date, say date = 20050104, and then sort the data by a certain order (the order that each stock was listed in the stock market).
I have another table called stock_code which stores the correct order:
+---------+-----------+
| code_id | code |
+---------+-----------+
| 1 | 000002.SZ |
| 2 | 000004.SZ |
| 3 | 600656.SH |
| 4 | 600651.SH |
| 5 | 600652.SH |
| 6 | 600653.SH |
| 7 | 600654.SH |
| 8 | 600602.SH |
| 9 | 600601.SH |
| 10 | 000005.SZ |
...
I want to sorted the selected data by stock_code(code_id), but I don't want to use join because it takes too much time. Any thoughts?
I tried to use field but it gives me an error, please tell me how to correct it or give me an even better idea.
select * from test
where date = 20050104 and code in (select code from stock_code order by code)
order by field(code, (select code from stock_code order by code));
Error Code: 1242. Subquery returns more than 1 row
You told us that you don't want to join because it takes too much time, but the following join query is probably the best option here:
SELECT t.*
FROM test t
INNER JOIN stock_code sc
ON t.code = sc.code
WHERE t.date = '20050104'
ORDER BY sc.code_id
If this really runs slowly, then you should check to make sure you have indices setup on the appropriate columns. In this case, indices on the code columns from both tables as well as an index on test.date should be very helpful.
ALTER TABLE test ADD INDEX code_idx (code)
ALTER TABLE test ADD INDEX date_idx (date)
ALTER TABLE code ADD INDEX code_idx (code)

SQL: Get row with highest ID, but if specific column is filled, get that row

i have a table called vouchers with many voucher codes inside. This is how the table looks like, with a few dummy data.
+-------+---------+-------------+------------+------------+
| gs_id | code | domain | start | ende |
+-------+---------+-------------+------------+------------+
| 1 | EXAMPLE | example.com | 1288263600 | 1291330740 |
| 2 | NULL | example.com | 1288604942 | 1293750000 |
| 3 | ABC123 | second.com | 1288604952 | 1298847600 |
| 4 | 456XYZ | second.com | 0 | 1288303200 |
+-------+---------+-------------+------------+------------+
Now i need an SQL statement which returns me:
1) Only one id per domain
2) If there are multiple entries per domain it should return the row with the highest id
That is already working with that Query:
SELECT MAX(gs_id) AS id,domain FROM vouchers WHERE start >= 0 AND start <= 1288263600 AND (ende = 0 || ende > 1418046119) GROUP BY domain
But i have one exception which is important to me:
If there are multiple entries for one domain and the column code of the entry with highest id is empty, AND there is one other voucher from that domain with non empty column code it should return that row.. If there are multiple rows which non empty code column it should here also return the row with highest gs_id
In my example ie expect that these rows are returned:
+----+---------+-------------+------------+------------+
| id | code | domain | start | ende |
+----+---------+-------------+------------+------------+
| 1 | EXAMPLE | example.com | 1288263600 | 1291330740 | because of code,
| 4 | 456XYZ | second.com | 1288604958 | 1288303200 | because of MAX(id)
+----+---------+-------------+------------+------------+
Row 2 should not be choosed because it has empty column code and there is another entry from same domein which has a non empty code.
What is the best way to do it?
Filter out the null code rows in the derived table and get the max gs_id. Then join the result to the main table.
select t.id, t.domain, v.code, v.start, v.ende
from (SELECT MAX(gs_id) AS id, domain
FROM vouchers
WHERE start >= 0 AND start <= 1288263600 AND (ende = 0 or ende > 1418046119)
and code is not null
GROUP BY domain) t
join vouchers v on t.domain = v.domain and t.id = v.gs_id

Retrieve all not logged in users from mysql tables (SQL query)

I have 2 tables listed below:
+-------------+-----------+--------------+-------------------------------+
| v3_customer |
+-------------+-----------+--------------+-------------------------------+
| customer_id | firstname | lastname | email |
+-------------+-----------+--------------+-------------------------------+
| 1 | Piet | Pizza | piet.pizza#example.com |
| 2 | Klaas | Hein | klaas.hein#example.com |
| 3 | Henk | Crowdcontrol | henk.crowdcontrol#example.com |
+-------------+-----------+--------------+-------------------------------+
+-------------+-------------+---------------+
| v3_customer_activity |
+-------------+-------------+---------------+
| activity_id | customer_id | key |
+-------------+-------------+---------------+
| 1 | 1 | login |
| 2 | 1 | order_account |
| 3 | 2 | login |
+-------------+-------------+---------------+
What i want is to select all customers which haven't logged in yet (note the login key in v3_customer_activity). So in this case that would be the customer with customer_id: 3
I'm using a mysql database.
I have tried using the following query:
SELECT DISTINCT v3_customer.customer_id, v3_customer.firstname, v3_customer.lastname, v3_customer.email FROM `v3_customer` JOIN v3_customer_activity ON v3_customer.customer_id = v3_customer_activity.customer_id WHERE v3_customer.customer_id != (SELECT v3_customer_activity.customer_id FROM v3_customer_activity)
In the hope it would iterate between the rows found in the subquery.
This resulted in an error telling me a subquery may not contain multiple rows.
TL;DR
What I want is to retrieve every customer from v3_customer who is not listed in the table v3_customer_activity
Try this:
select v3_customer.* from v3_customer
left join v3_customer_activity on v3_customer.customer_id=v3_customer_activity.customer_id
where v3_customer_activity.customer_id is null;
Left join v3_customer table with v3_customer_activity and filter records which are not matched.
select v3_customer.* from v3_customer
where v3_customer.customer_id not in (SELECT v3_customer_activity.customer_id FROM v3_customer_activity)

In MYSQL, how do I get a LEFT JOIN to return every row in one table, and a flag if there were any matching rows in another table?

Basically, I have two tables, admin_privilege and admin_roll_privilege. I'm trying to write a query to get every row from admin_privilege, and if there is a row in admin_roll_privilege with a matching admin_privilege_id AND a matching admin_roll_id, to set a new column to 1. So far, I have this:
SELECT ap.*,
IF(arp.admin_privilege_id IS NULL,0,1) AS has_privilege
FROM admin_privilege ap LEFT JOIN admin_roll_privilege arp
ON ap.admin_privilege_id=arp.admin_privilege_id
WHERE arp.admin_roll_id=3
OR arp.admin_roll_id IS NULL;
This works in every case except where there are no matching rows admin_roll_privilege.
See Example:
+---------------+--------------------+
| admin_roll_id | admin_privilege_id |
+---------------+--------------------+
| 1 | 2 |
| 1 | 3 |
+---------------+--------------------+
+--------------------+------------------------+
| admin_privilege_id | admin_privilege_name |
+--------------------+------------------------+
| 1 | Access Developer Tools |
| 4 | Edit System Settings |
| 2 | Edit User Profiles |
| 3 | Resolve Challenges |
+--------------------+------------------------+
Querying for WHERE admin roll id=1 works as expected:
+--------------------+------------------------+---------------+
| admin_privilege_id | admin_privilege_name | has_privilege |
+--------------------+------------------------+---------------+
| 1 | Access Developer Tools | 0 |
| 4 | Edit System Settings | 0 |
| 2 | Edit User Profiles | 1 |
| 3 | Resolve Challenges | 1 |
+--------------------+------------------------+---------------+
But, if i query for admin_roll_id=3, i only get two rows returned:
+--------------------+------------------------+---------------+
| admin_privilege_id | admin_privilege_name | has_privilege |
+--------------------+------------------------+---------------+
| 1 | Access Developer Tools | 0 |
| 4 | Edit System Settings | 0 |
+--------------------+------------------------+---------------+
How can I get this query to return all 4?
Edit: This is what ended up working, moving the condition to the on clause:
SELECT ap.*,
IF(arp.admin_privilege_id IS NULL,0,1) AS has_privilege
FROM admin_privilege ap LEFT JOIN admin_roll_privilege arp
ON (ap.admin_privilege_id=arp.admin_privilege_id AND arp.admin_roll_id=1)
Move the appropriate conditions from the WHERE clause to the ON clause.
You are not returning all rows by using the WHERE clause on the entire statement.
Turn the LEFT JOIN into a subselect on wich you can add the WHERE clause you need.
SELECT ap.admin_privilege_id
, ap.admin_privilege_name
, IF(arp.admin_privilege_id IS NULL,0,1) AS has_privilege
FROM admin_privilege ap
LEFT OUTER JOIN (
SELECT admin_privilege_id
FROM admin_roll_privilege arp
WHERE arp.admin_roll_id = 3
) arp ON arp.admin_privilege_id = ap.admin_privilege_id