SQL where not in LIKE - mysql

I have a list of email addresses in one table and a list of domains in another table. I want to query the email from the email table ONLY if their email doesn't have a domain set in the domains table.
email domains
------ -------
a#google.com yahoo.com
a#yahoo.com ebay.com
a#ebay.com
a#gmail.com
So only a#google.com and a#gmail.com should be the result of the query.
The issues im running into is I have about 6 million rows in the emails table and it is taking substantially long to query even with proper indexes. Is there a better way to go about this? I also have a feeling using concat isn't helping because when you use a function it doesn't use indexes anymore?
Here is my query:
SELECT
email
FROM
emails
LEFT JOIN `domains` ON emails.email LIKE CONCAT( '%', domains.domain, '%' )
WHERE
AND `domains`.`domain` IS NULL

I would use a subquery like this:
select e.*
from email e
where not exists (select 1
from domains d
where e.email like concat('%', d.domain)
);
However, you might be able to get better performance with this version:
select e.*
from email e
where not exists (select 1
from domains d
where substring_index(e.email, '#', -1) = d.domain
);
The equality may make it possible to use an index on domains(domain).

Related

SQL use value from local table to reference table in different DB

This may not be possible ;^)
I'm trying to pull together billing and performance data from separate client dbs. The "core" db has a table like so:
client_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
client_db_name VARCHAR(100),
...
Each client_db has a table, orders, with columns order_date and order_total.
Conceptually, I'd like to be able to use the client_db_name in a query:
SELECT SUM(order_total) AS sales
FROM {client_db_name}.orders AS o
WHERE o.order_date LIKE '2021%'
Questions:
is this even possible?
if so, how would I JOIN this query? What would the ON be?
Many thanks!
No interpolation.
A Stored procedure might be able to perform the interpolation via CONCAT, PREPARE and EXECUTE.
Wouldn't it be better the have all the customers in the same table? That would make it all easier.
You can make a dynamic query, a query that is the result of another query.
SELECT GROUP_CONCAT(query_piece SEPARATOR ' UNION ALL ')
FROM (SELECT CONCAT("SELECT '", client_db_name, "' AS client, SUM(order_total) AS sales FROM ", client_db_name, '.orders o WHERE YEAR(o.order_date) = 2021') AS query_piece
FROM core.clients) AS sq
My query will return a dynamic query like this (I have two clients into core.clients table: a and b):
SELECT 'a' AS client, SUM(order_total) AS sales FROM a.orders o WHERE YEAR(o.order_date) = 2021
UNION ALL
SELECT 'b' AS client, SUM(order_total) AS sales FROM b.orders o WHERE YEAR(o.order_date) = 2021
The dynamic query will return something like this:
client
sales
a
50.00
b
100.00

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.

MySQL if result null change query

what if I have two strings: "123" and "abc". I want to select username if there's username "123" then choose it, if not found (null) then select username where "abc"
I have a table called USERS, this table responsibility with workflow engine account. I want to show columns in USERS:
username
email
usr_firstname
usr_lastname
I am using concat to merge column 3 and 4 with space between it. In the office, there are 2 types of employee:
origin/internal employee
outsource/partner employee
Origin employee login into every system using LDAP (FirstName.LastName), but outsource or partner employee login individually just for our workflow engine using employee identity number.
In this case, if I use something like:
Where username = 'employeenumber' or username = 'LDAPacc' the result is both account (used and unused for outsource) they appear. I want to show just 1 rows and 1 query but it's work with internal or even outsource (they will got data correctly for outsource).
You can use like this query;
SELECT *
FROM TABLE
WHERE username IN ('123', 'abc')
AND (username='123' OR NOT EXISTS(SELECT * FROM TABLE WHERE username='abc'))
You could use COALESCE.
COALESCE selects the first non null value out of the ones supplied.
So you could use....
SELECT COALESCE(String_123, string_ABC);
If string_123 has a value it will select that, otherwise it will select string_ABC unless of course they are both null.
So to be safe include a default value.......
SELECT COALESCE(String_123, string_ABC, string_Default);
I've found when I tested my logic to mysql tryit editor by w3schools and It's worked properly what I need. Here's my query:
SELECT * FROM Customers WHERE CustomerID = 'zz' OR (NOT EXISTS (SELECT * FROM Customers WHERE CustomerID = 'zz') AND CustomerID = '3')
let's say CustomerID is equivalent to my username column, then I tried to swap 'zz' and '3' value and it's still works. I hope there's more simple query than this

mysql search in main query and subquery

Having the following query where i am trying to search in a query, i might be writing a wrong syntax, but i am not sure how to correct it. I want to search for the text in the main table and the subquery table too.
here is my query
select mytable.*
from mytable
where spam = 0
and deleted = 0
and draft = 0
and (subject like '%guss%' or body like '%guss%' or
(select CONCAT(users.firstname,' ', users.lastname) as fname,users.email
from users where firstname like '%guss%' or lastname like 'guss'))
and id = 24
order by id desc
getting this Error
[Err] 1241 - Operand should contain 1 column(s)
Update #1
select mytable.*
from mytable
where spam = 0
and deleted = 0
and draft = 0
and (subject like '%eli%' or body like '%eli%' or
(select users.firstname
from users where firstname like '%eli%') or
(select users.lastname
from users where lastname like '%eli%'))
and id_receiver = 24
order by id desc
Here the error:
(select CONCAT(users.firstname,' ', users.lastname) as fname,users.email
from users where firstname like '%guss%' or lastname like 'guss'))
In your subquery you can return only one column
This is the first column: CONCAT(users.firstname,' ', users.lastname) as fname
This is the second column: users.email
You put a 2-column result as an operand of OR expression.
This causes the issue. It should return one column or be compared to something.
But if you want to search in both tables I guess UNION would be something you need.
Though I think this is wrong I don't believe mytable.id relates to users.id but that's what a comment said...
SELECT Distinct mytable.*
FROM mytable
INNER JOIN users
on myTable.ID = users.Id
WHERE mytable.spam = 0
and mytable.deleted = 0
and mytable.draft = 0
and CONCAT_WS(mytable.subject, mytable.body, users.firstname, users.lastname) like '%eli%'
and mytable.id_receiver = 24
ORDER BY mytable.id desc
I removed the or using string concatenation. We want any record having the text of 'eli' in any of the columns subject, body, firstname, lastname. The system would have to loop through each column checking for a %eli% value. In theory up to 4 loops. By adding all the columns together to form one string and checking for eli we eliminate extra looping the engine would have to do at an overhead of the string concatenation. This should be faster.
I used distinct as I don't know what results you want and if the join will result in multiple records that serve no purpose. Since a * is being used I couldn't use a group by properly.
I joined to users assuming that you only want records in mytable that link to a user. This may be a wrong assumption.

MySQL alias for SELECT * columns

I'm creating a view that is using data that comes from the same table twice. As a result, same column names appear twice.
Thus, i need to give aliases to these columns. If i were to do it, i'd write it as:
SELECT u.* as 'one_*', u2.* as 'two_*'
FROM users u
LEFT JOIN relationships r ON u.id=r.id_one
LEFT JOIN users u2 ON r.id_two=u2.id
But that doesn't work. Thanks for your help!
EDIT:
Here's the data i'm actually getting:
| id | name | id | name |
1 john 2 alex
Here's the data i'd like to get (while still using a SELECT u.*, u2.*):
| id | name | brother_id | brother_name |
1 john 2 alex
You can't use * with an alias. Aliases can be used for individual columns.
You'll have to alias each column instead..
So unfortunately, if you have a lot of columns, you'll need to go:
SELECT u.col1 AS u_col1
, u.col2 AS u_col2
, u.col3 AS u_col3
-- etc
, u2.col1 AS u2_col1
, u2.col2 AS u2_col2
, u2.col3 AS u2_col3
-- etc
FROM table1 AS u
-- INNER JOIN / LEFT OR RIGHT OUTER JOIN / ,
table2 AS u2
Try using a UNION query:
e.g.
select a.typeid, a.typename from MYTABLE a where a.typeid=3 UNION select a.typeid, a.typename from MYTABLE a where a.typeid=4
Can you not just use SELECT * and then in your code refer to u.field1 and u2.field2?
I know this is an old question but I recently had the problem and came up with a solution.
First query the table and get the names of the columns
ex.
"SHOW COLUMNS FROM $table_name"
then use a loop to concat a prefix to the column name
ex
foreach ($all_columns as $the_column){
$alias_select .= ', '.$table_name.'.'.$the_column['Field'].' alias_'.$the_column['Field'];
}
then just put this string into your query and you will get another set of values all with the prefix_column_name.
SELECT alias.* does certainly work in mysql >= 5.6