Select from another table if result not matched - mysql

This has been asked but I dont quite match existing questions with my case.
I have two tables, the first table is credentials with id username and email
and an email-alias table with user-id (which corresponds to credentials.id) and email. Emails in credentials are more often "user.name#domain.com" while in alias they'd be "usern#domain.com".
All I have now is
SELECT `username` FROM `credentials` WHERE `email` LIKE ?
But the email will not always match if I query with "usern#domain.com". What I want to do is get the username with one query which would fall back to email-alias and use "usern#domain.com" to get an user-id from there to be used again in credentials to match a username
The pitfall is that the supplied email could be either an aliased one "usern#.." or "user.name#.."
mysql> describe `email-alias`;
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user-id | int(11) | NO | UNI | NULL | |
| email | varchar(100) | YES | | NULL | |
+---------+--------------+------+-----+---------+----------------+
mysql> describe `credentials`;
+-----------------+--------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+--------------+------+-----+---------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(100) | NO | UNI | NULL | |
| email | varchar(100) | NO | | NULL | |
+-----------------+--------------+------+-----+---------------------+----------------+

Your question is kind of confusing but I think what you're trying to do is select username from the first table if the email exists and if it doesn't select from the 2nd table if it exists. You would use subqueries for that. Hope this helps.
SELECT `c.username`
FROM credentials c
WHERE c.email = 'usern#domain.com' OR c.id =
(SELECT `e.user-id` from email-alias e WHERE e.email = "usern#domain.com")

I'm not sure if I understand your question clearly because it's too confusing. But let me give this a try. You need to join the table using INNER JOIN.
SELECT `username`
FROM credentials a
INNER JOIN email_alias b
on a.ID = b.userID
WHERE b.email = 'usern#domain.com'
UPDATE 1
SELECT `username`
FROM credentials a
INNER JOIN email_alias b
on a.ID = b.userID
WHERE b.email LIKE '%usern#%'

Related

sql query for fetching leaderboard from two tables

I have two MySql table user and marks
User table:
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| firstName | varchar(255) | YES | | NULL | |
| lastName | varchar(255) | YES | | NULL | |
| email | varchar(55) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
Marks Table
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| user_id | int | NO | | NULL | |
| subject_id | varchar(255) | YES | | NULL | |
| score | int | YES | | NULL | |
| subject_name | varchar(225) | YES | | NULL | |
+--------------+--------------+------+-----+---------+----------------+
I want to fetch details(userid, firstName and lastName) of top 10 users with the highest marks in descending order.
Marks of the user is defined as sum of all scores a user has in different subjects.
I am really confused which join is to be used here, I am new to MySql and this query is kind of challenging for me, Hope you understood the problem.
Please let me know if you have any suggestion, Thank You
You can join the two tables together by id=user_id, then you group the result by id, sort by the total marks per id, then take the top 10 results.
If you wanted a result even if the user had no marks at all, change JOIN to LEFT JOIN, this will still give you a result from the first table even if there are no results from the second.
SELECT u.id, u.firstName, u.lastName, SUM(m.score) AS TotalScore
FROM [User] AS u
JOIN Marks AS m ON m.user_id = u.id
GROUP BY u.id, u.firstName, u.lastName
ORDER BY SUM(m.score) DESC
LIMIT 10;
You should inner join the table Marks on the id from table User with user_id from the table Marks:
SELECT user_id, firstName, lastName
FROM User
INNER JOIN Marks
ON User.id = Marks.user_id
Here is a helpful resource for SQL joins

Getting rows from two tables with a join and where clause

I am needing to get users from a 'users' table that are not equal to my username, but also not in a blocked table as the 'blockee'.
Using only the users table, I am doing:
SELECT * FROM users WHERE username != <myself> AND interests LIKE <string>;
However, since I've added the additional 'blocked' table, I'm needing to further filter them.
mysql> describe users;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(255) | NO | | NULL | |
| password | varchar(255) | NO | | NULL | |
| email | varchar(255) | NO | | NULL | |
| city | varchar(60) | NO | | NULL | |
| state | varchar(60) | NO | | NULL | |
| lat | decimal(8,6) | YES | | NULL | |
| lng | decimal(9,6) | YES | | NULL | |
| interests | text | YES | | NULL | |
| hash | varchar(32) | YES | | NULL | |
| active | int(1) | YES | | NULL | |
| avatar | varchar(255) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
12 rows in set (0.00 sec)
mysql> describe blocked;
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| blocker | varchar(255) | NO | | NULL | |
| blockee | varchar(255) | NO | | NULL | |
+---------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
I attempted:
SELECT * FROM users JOIN blocked ON users.username = blocked.blocker WHERE username != 'myself' AND blocked.blockee IS NULL;
A pseudo-query would look like:
"give me every user that's not equal to my username, but is also not a blockee where I am the blocker"
So, if I had these values in the blocked table:
+----+-----------+----------+
| id | blocker | blockee |
+----+-----------+----------+
| 1 | myself | testuser |
+----+-----------+----------+
It would return everyone that is not testuser and had the same values in the interests column as the LIKE clause.
I hope this makes sense. I'm stuck.
This query will give you the list of all users who are not you and are not blocked by you. You can then add other conditions (such as AND interests LIKE <string>) to the WHERE clause as necessary.
SELECT u.*
FROM users u
LEFT JOIN blocked b
ON b.blockee != u.username AND b.blocker = 'myself'
WHERE u.username != 'myself' AND b.blockee IS NOT NULL
Demo on dbfiddle
Here's the query that will do what you're looking for:
select *
from users u
where u.username != 'myself'
and not exists (
select 1
from blocked b
where b.blocker = 'myself'
and b.blockee = u.username
)
and u.interests like '%what%';
I put together an example you can execute to see how it behaves: https://rextester.com/GPEC60793
Here's a copy of the entire demo:
drop table if exists demo_users;
drop table if exists demo_blocked;
create table demo_users (id int auto_increment, username varchar(255), interests text, primary key (id));
create table demo_blocked (id int auto_increment, blocker varchar(255), blockee varchar(255), primary key (id));
insert into demo_users (username, interests)
values ('myself', 'whatever'),
('testuser', 'blah'),
('somebody', 'foo'),
('nobody', 'whatfor');
insert into demo_blocked (blocker, blockee)
values ('myself', 'testuser');
select * from demo_users;
select * from demo_blocked;
select *
from demo_users u
where u.username != 'myself'
and not exists (
select 1
from demo_blocked b
where b.blocker = 'myself'
and b.blockee = u.username
)
and u.interests like '%what%';
drop table demo_users;
drop table demo_blocked;
If you don't care about performance, you can try sub query.
SELECT * FROM users WHERE username != 'myself' and user.username not in (select locker from blocked);
I think it will work correctly for you
This should work - but again I don't have your test data to test it against.
Cheers
SELECT *
FROM users
WHERE ( 1 = 1 )
AND ( username ! = 'myself OR username not in ( select username from blocked ))
AND interests like '%string%';
in the predicate yo may need to change the OR to an AND - as I said no data to test my logic.
HTH!
First get a list of your blockee, then try to match those with your user table, if you cant find a match you will have null blockee, and those are the users you want.
SELECT u.*
FROM users u
LEFT JOIN ( SELECT b.blockee
FROM blocked b
WHERE b.blocker = 'myself') as b
ON o.username = b.blockee
WHERE b.blockee IS NULL
AND u.interests LIKE <string>
AND u.username != 'myself' -- if blocker cant be the same as blockee
-- you dont need this one

MySQL - UPDATE one column based on results of a SELECT when the SELECT returns multiple columns

I've read MySQL - UPDATE query based on SELECT Query and am trying to do something similar - i.e. run an UPDATE query on a table and populate it with the results from a SELECT.
In my case the table I want to update is called substances and has a column called cas_html which is supposed to store CAS Numbers (chemical codes) as a HTML string.
Due to the structure of the database I am running the following query which will give me a result set of the substance ID and name (substances.id, substances.name) and the CAS as a HTML string (cas_values which comes from cas.value):
SELECT s.`id`, GROUP_CONCAT(c.`value` ORDER BY c.`id` SEPARATOR '<br>') cas_values, GROUP_CONCAT(s.`name` ORDER BY s.`id`) substance_name FROM substances s LEFT JOIN cas_substances cs ON s.id = cs.substance_id LEFT JOIN cas c ON cs.cas_id = c.id GROUP BY s.id;
Sample output:
id | cas_values | substance_name
----------------------------------------
1 | 133-24<br> | Chemical A
455-213<br>
21-234
-----|----------------|-----------------
2 999-23 | Chemical B
-----|----------------|-----------------
3 | | Chemical C
-----|----------------|-----------------
As you can see the cas_values column contains the HTML string (which may also be an empty string as in the case of "Chemical C"). I want to write the data in the cas_values column into substances.cas_html. However I can't piece together how to do this because other posts I'm reading get the data for the UPDATE in one column - I have other columns returned by my SELECT query.
Essentially the problem is that in my "sample output" table above I have 3 columns being returned. Other SO posts seem to have just 1 column being returned which is the actual values that are used in the UPDATE query (in this case on the substances table).
Is this possible?
I am using MySQL 5.5.56-MariaDB
These are the structures of the tables, if this helps:
mysql> DESCRIBE substances;
+-------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-----------------------+------+-----+---------+----------------+
| id | mediumint(8) unsigned | NO | PRI | NULL | auto_increment |
| app_id | varchar(8) | NO | UNI | NULL | |
| name | varchar(1500) | NO | | NULL | |
| date | date | NO | | NULL | |
| cas_html | text | YES | | NULL | |
+-------------+-----------------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)
mysql> DESCRIBE cas;
+-------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+----------------+
| id | mediumint(8) unsigned | NO | PRI | NULL | auto_increment |
| value | varchar(13) | NO | UNI | NULL | |
+-------+-----------------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)
mysql> DESCRIBE cas_substances;
+--------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| cas_id | mediumint(8) unsigned | NO | MUL | NULL | |
| substance_id | mediumint(8) unsigned | NO | MUL | NULL | |
+--------------+-----------------------+------+-----+---------+----------------+
3 rows in set (0.02 sec)
Try something like this :
UPDATE substances AS s,
(
SELECT s.`id`,
GROUP_CONCAT(c.`value` ORDER BY c.`id` SEPARATOR '<br>') cas_values,
GROUP_CONCAT(s.`name` ORDER BY s.`id`) substance_name
FROM substances s
LEFT JOIN cas_substances cs ON s.id = cs.substance_id
LEFT JOIN cas c ON cs.cas_id = c.id
GROUP BY s.id
) AS t
SET s.cas_html=t.cas_values
WHERE s.id = t.id
If you don't want to modify all the value, the best way to limit the update to test it, is to add a condition in the where, something like that :
...
WHERE s.id = t.id AND s.id = 1

SQL join not working (or very slow)

I have the following tables in mysql:
Table A:
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| sid | varchar(50) | YES | | NULL | |
| type | int(11) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
Table B:
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| channel | varchar(20) | YES | | NULL | |
| sid | varchar(50) | YES | | NULL | |
| type | varchar(20) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
I want to find the rows from A that have an entry in B with the same sid. I tried the following Join command:
SELECT A.sid FROM A join B on A.sid=B.sid;
This query never gives me the answer.
Tabe A has 465420 entries and table B has 291326 entries.
Why does it not work?
Are there too many entries?
Or does it have anything to do with the fact that I have no primary keys assigned?
Your query is fine. You would appear to need an index. I would suggest B(sid).
You can also write the query as:
select a.sid
from a
where exists (select 1 from b where a.sid = b.sid);
This will not affect performance -- unless there are lots of duplicates in b -- but it will eliminate issues caused by duplicates in b.
Try
SELECT A1.sid
FROM (select A.sid from A order by sid) A1
join (select B.sid from B order by sid) B1
on A1.sid=B1.sid;
Else above holds true. You need index.

MYSQL query return empty result

I need to look up email preferences for users.
This table contains the types of email a user can receive, broken down by category.
email_preferences_categories
+----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | text | YES | | NULL | |
| overview | text | YES | | NULL | |
+----------+------------------+------+-----+---------+----------------+
This table contains their preference for receiving various types. If they haven't set their preferences, this table won't have any rows for them.
email_preferences
+------------+---------------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| user_id | int(10) unsigned | NO | | NULL | |
| name | text | YES | | NULL | |
| frequency | enum('Daily','Monthly','None') | YES | | Daily | |
+------------+---------------------------------+------+-----+---------+----------------+
I need to construct a MYSQL query that returns the name and frequency corresponding to the email preferences for a given user.
SELECT name, frequency
FROM email_preferences
LEFT JOIN email_preferences_categories using (name)
WHERE user_id = 42
Where I'm having trouble: If the user hasn't set their preferences, this query doesn't return any rows. I would like it to return the default of 'Daily' for email categories that are missing.
Change LEFT JOIN to RIGHT JOIN.
...
FROM email_preferences
RIGHT JOIN email_preferences_categories
...
Or alternatively you can swap the tables around:
...
FROM email_preferences_categories
LEFT JOIN email_preferences
...
These two options both do the same thing - ensure that you get all rows from email_preferences_categories even if there is no matching row in email_preferences.
You also need to change the join condition as you already noticed.
I would like it to return the default of 'Daily' for email categories that are missing.
You can use IFNULL:
SELECT name, IFNULL(frequency, 'Daily') AS frequency
This query doesn't need a WHERE clause. It needs a more restrictive JOIN. Here is the full query combined with Mark Byers answer above.
SELECT email_preferences_categories.name, IFNULL(frequency, 'Daily') AS frequency
FROM email_preferences_categories
LEFT JOIN email_preferences
ON email_preferences.name = email_preferences_categories.name
AND user_id = 42;