Another select statement if the value is NULL - mysql

I want to send completely another query if the value of the given field is NULL is it possible ?
Something like this :
IF the value is null
SELECT * FROM `locations`
ELSE
SELECT * FROM `companies`
ENDIF
I want to use the output of this query in another query. So it is going to be like this. If the field is not null in the first table take it else take another field from another table and lastly another query from the result of this if.

SELECT * FROM `locations` WHERE 1 = (CASE WHEN value IS NULL THEN 1 ELSE 0 END)
UNION ALL
SELECT * FROM `companies` WHERE 1 = (CASE WHEN value IS NOT NULL THEN 1 ELSE 0 END)
not tested but should work.

Similar to aleroot's contribution but avoiding the (IMHO) ugly case: (also untested)
SELECT * FROM (SELECT * FROM locations WHERE value IS NULL)
UNION ALL
SELECT * FROM (SELECT * FROM companies WHERE value IS NOT NULL)

SELECT
CASE WHEN L.value IS NULL THEN C.x ELSE L.x END AS x,
CASE WHEN L.value IS NULL THEN C.y ELSE L.y END AS y,
CASE WHEN L.value IS NULL THEN C.z ELSE L.z END AS z,
...
FROM
locations L
LEFT JOIN companies C
ON L.LocationID=C.LocationID;
I do not know how you will have to join your tables in your case, but this gives you an idea of how you can solve the problem. In my solution, the decision whether the fields are taken from locations or companies, is taken on a record-by-record basis.

If i understand, you want to retrive one, or another query, with different table definitions, in one call, if something is null? i think is not a good practice...
Using MysqlI and a stored procedure, you can read the last select executed, but is weird:
DELIMITER //
CREATE PROCEDURE make_weird_query() BEGIN
IF something is null THEN
SELECT * FROM `locations`;
ELSE
SELECT * FROM `companies`;
ENDIF;
END;//
In PHP:
$query = $mysql->query("CALL make_weird_query()");
while($row = $query->fetch_assoc()){
// ...
}

Related

If value exists in other table, return another value from that table

I currently have the following SQL query:
SELECT video_calls.initiated_user_id AS user_id,
(CASE
WHEN EXISTS
(SELECT *
FROM patients
WHERE patients.id = video_calls.initiated_user_id)
THEN 'patient'
ELSE (CASE
WHEN EXISTS
(SELECT *
FROM backend_users
WHERE backend_users.id = video_calls.initiated_user_id)
THEN "%%backend%%"
ELSE "unknown"
END)
END) AS user_type
FROM video_calls
WHERE id='7f350a98-93d3-4d21-80a8-6cda3e47a4c0'
UNION
SELECT user_id,
user_type
FROM channel_joins
WHERE channel_id='7f350a98-93d3-4d21-80a8-6cda3e47a4c0'
In the line, where it currently says THEN "%%backend%%" I'd like to return the column backend_users.backend_type instead, for the corresponding row where the value video_calls.initiated_user_id has been found. I suppose I need to work with a JOIN here, but I currently can't figure out where exactly.
You are already using a correlated subquery. You can use that to get the value:
ELSE (SELECT COALESCE(MAX(bu.backend_type), 'unknown')
FROM backend_users bu
WHERE bu.id = video_calls.initiated_user_id
)
Note the use of MAX(). This ensures that exactly one value is returned. If no rows match, the MAX() returns NULL, so 'unknown' is returned.
This has one slight nuance from your pseudo-code. If the matching row is NULL, then this returns 'unknown' rather than NULL. If that is an issue, the logic in the subquery can be tweaked.

MySQL replace NULL to some value

I have the following stored procedure
BEGIN
SELECT kids.*, SUM(point) as `point_sum`
FROM kids
LEFT JOIN tasks
ON kids.id = tasks.kid_id
WHERE kids.user_id = IN_user_id
GROUP BY kids.name;
END
This statement works fine.
My Question: the SUM(point) for new users are typically NULL because there is no submitted value yet to be summed.
What I want is if SUM(point) is NULL then it should return value like 0 but otherwise it should present the sum. I have looked around and not sure how to fix it, any good ideas?
You could use the coalesce function:
SELECT kids.*, COALESCE(SUM(point), 0) as `point_sum`
FROM kids
LEFT JOIN tasks
ON kids.id = tasks.kid_id
WHERE kids.user_id = IN_user_id
GROUP BY kids.name;
All you really need is IFNULL():
SELECT kids.*, IFNULL(SUM(point), 0) AS point_sum
That converts NULL to the supplied value, in this case 0.

MySQL - tell if column _all_ has same value

I'm trying to write a query like
if (select count(*) from Users where fkId=5000 and status='r') =
(select count(*) from Users where fkId=5000) then ..
in just one query.
What this means is, if all the rows that have fkId=5000 also have status=r, then do something.
There can be any number of rows with fkId=5000, and any fraction of those rows could have status=r, status=k, status=l, status=a etc. I'm interested in the case where ALL the rows that have fkId=5000 also have status=r (and not any other status).
The way I'm doing it now is
how many rows with id=5000 and status = 'r'?
how many rows with id=5000?
are those numbers equal? then ..
I'm trying to figure out how to rewrite this query using only 1 query, instead of 2. Keyword ALL didn't seem to be able to write such a query (<> ALL is equivalent to NOT IN). I tried a couple of GROUP BY formulations but could not get the correct result to appear.
The most efficient way to do this is:
if not exists (select 1
from users
where fkid = 5000 and (status <> 'r' or status is null)
)
This will stop the query at the first non-matching row.
I suggest you to check for any rows with status not equal to 'r'
SELECT count(*)>0 FROM Users WHERE fkId = 5000 AND status != 'r'
In the following case, if the number 1 is "true" (which it is) then you'll get Yes back, and if not you'll get No back:
SELECT IF(1, 'Yes', 'No') AS yesorno
(Go ahead -- try it!)
In your case however, the following would be more appropriate:
SELECT IF (
(SELECT COUNT(*) FROM Users WHERE fkId=5000 AND status IN('r') AND status NOT IN('1', 'a', 'k')) = (SELECT COUNT(*) FROM Users WHERE fkId=5000),
'They are equal.',
'They are not equal.'
)
AS are_they_equal
By adding AS, you can manipulate the name of the "column" that's returned to you.
Hope that helps... Also, see this page if you'd like more info.
:)
EASY!
Simply join back to the same table. Here is the complete code for testing:
CREATE TABLE Users(id int NOT NULL AUTO_INCREMENT, fkID int NOT NULL, status char(1), PRIMARY KEY (id));
INSERT Users (fkID, status) VALUES (5000, 'r');
INSERT Users (fkID, status) VALUES (5000, 'r');
INSERT Users (fkID, status) VALUES (5000, 'r');
-- The next query produces "0" to indicate no miss-matches
SELECT COUNT(*) FROM Users u1 LEFT JOIN Users u2 ON u1.id=u2.id AND u2.status='r' WHERE u1.fkID=5000 AND u2.id IS NULL;
-- now change one record to create a miss-match
UPDATE Users SET status='l' WHERE id=3 ;
-- The next query produces "1" to indicate 1 miss-match
SELECT COUNT(*) FROM Users u1 LEFT JOIN Users u2 ON u1.id=u2.id AND u2.status='r' WHERE u1.fkID=5000 AND u2.id IS NULL;
DROP TABLE Users;
So all you need to test for in the result is that it's 0 (zero) meaning everything has fkID=5000 also has status='r'
If you properly index your table then joining back to the same table is not an issue and certainly beats having to do a 2nd query.
Besides the NOT EXISTS version - which should be the most efficient as it does no counting at all and exits as soon as it finds a value that doesn't match the conditions, there is one more way, that will work if status is not nullable and will be efficient if there is an index on (fkId, status):
IF EXISTS
( SELECT 1
FROM Users
WHERE fkId = 5000
HAVING MIN(status) = 'r'
AND MAX(status) = 'r'
)
There is one difference though. The above will show false if there are no rows at all with fkId=5000, while the NOT EXISTS version will show true - which is probably what you want anyway.

mysql query taking too long

I am new to advanced queries so I likely have something conceptually wrong because when the database has over 1 million records I get this response rom my query...
ERROR 2013: Lost connection to MySQL server during query
Yes! It actually takes so long that it pukes before it finishes.
My query is this...
SELECT users.username,
table_1.field_abc, table_1.field_def,
table_2.field_ghi, table_2.field_jkl
FROM users
LEFT JOIN table_1 ON table_1.username = users.username
LEFT JOIN table_2 ON table_2.username = users.username
WHERE
table_1.field_abc REGEXP "(spork|yellow)" OR
table_1.field_def REGEXP "(spork|yellow)" OR
table_2.field_ghi REGEXP "(spork|yellow)" OR
table_2.field_jkl REGEXP "(spork|yellow)"
GROUP BY users.username
ORDER BY
(
( CASE WHEN table_1.field_abc LIKE "%spork%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_1.field_abc LIKE "%yellow%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_1.field_def LIKE "%spork%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_1.field_def LIKE "%yellow%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_2.field_ghi LIKE "%spork%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_2.field_ghi LIKE "%yellow%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_2.field_jkl LIKE "%spork%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_2.field_jkl LIKE "%yellow%" THEN 1 ELSE 0 END )
)DESC;
I posted a sample dataset (with only a few records) at http://sqlfiddle.com/#!2/cbbda/28
The sample at sqlfiddle runs quick because there are only a few records but I tried duplicating records on my own server and the query ran quick with only a few records and extremely slow after I added a million records.
Is there any possible way to get my results quick?
Well folks... With your help we have a solution... See... http://sqlfiddle.com/#!2/fcfbd/5
BUT I DO STILL HAVE A QUESTION...
I altered the table to add the indexes...
ALTER TABLE `users` ADD FULLTEXT ( `username` );
ALTER TABLE `table_1` ADD FULLTEXT ( `field_abc`,`field_def` );
ALTER TABLE `table_2` ADD FULLTEXT ( `field_ghi`,`field_jkl` );
I then took the advice of #Barmar and changed the code to this...
SELECT users.username,
table_1.field_abc, table_1.field_def,
table_2.field_ghi, table_2.field_jkl
FROM users
LEFT JOIN table_1 ON table_1.username = users.username
LEFT JOIN table_2 ON table_2.username = users.username
WHERE
MATCH(table_1.field_abc,table_1.field_def,table_2.field_ghi,table_2.field_jkl)
AGAINST ("spork yellow" IN BOOLEAN MODE)
GROUP BY users.username
ORDER BY
(
( CASE WHEN MATCH(table_1.field_abc) AGAINST ("spork" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_1.field_abc) AGAINST ("yellow" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_1.field_def) AGAINST ("spork" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_1.field_def) AGAINST ("yellow" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_2.field_ghi) AGAINST ("spork" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_2.field_ghi) AGAINST ("yellow" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_2.field_ghi) AGAINST ("spork" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_2.field_ghi) AGAINST ("yellow" IN BOOLEAN MODE) THEN 1 ELSE 0 END )
)DESC;
With over 1,000,000 records in my real database, I got my result in 6.5027 seconds. That is A LOT better than... well, taking so long that it puked!
My only question now is... Why does it only work with IN BOOLEAN MODE and not the other 2 options mentioned at http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html#function_match or http://dev.mysql.com/doc/refman/5.5/en/fulltext-search.html?
I don't think so - with this table as-is, I doubt you'll make that run fast with all of those LIKEs on them. Those have to run a ridiculous number of times.
If those values are fixed, then you can add new columns to the table called abc_like_yellow and abc_like_spork, etc., and populate those values one time, then you can easily query off of that column.
But if you're trying to do this dynamically, you might be out of luck.
Since we're joining on username it is likely that an index on this column will speed things up.
Also, are you able to use an inner join as opposed to a left join? This can also speed up the query to quite a large extent.
And finally, if necessary, the ordering can be done in memory as opposed to asking the database to do it (ie order the result set after it is returned).
I was using my first solution but found that it gave some false positives that I could not figure out so I came up with this...
(SELECT username, MATCH(field_abc,field_def) AGAINST ("spork yellow" IN BOOLEAN MODE) AS score FROM table_1 HAVING score>0)
UNION ALL
(SELECT username, MATCH(field_ghi,field_jkl) AGAINST ("spork yellow" IN BOOLEAN MODE) AS score FROM table_2 HAVING score >0)
Since each record was returned separately and I can't use GROUP BY I added this PHP code after my query finished:
while($row = mysql_fetch_array($result) )
{
if( in_array($row['username'],$usernames) )
{
$usernames_count[$row['username']] += $row['score'];
}
else
{
array_push($usernames,$row['username']);
$usernames_count[$row['username']]=$row['score'];
}
}
arsort($usernames_count); // Sort the results high->low
foreach($usernames_count as $key=>$value)
{
echo "Username: ".$key." had a score of ".$value." in the search results<br/>";
}
It now seems so simple compared to the other attempts I made.
When your server has to scan through millions of entries, it simply may not be powerful enough to process the query quickly.
In general, to improve the speed of your website, you could try CloudFlare
If you are specifically trying to speed up your SQL, Google Cloud SQL may be able to help. Google's powerful servers are designed to scan through billions of SQL entries, for example when a Google search is performed.
As long as there are no errors being returned, the above two services will help to dramatically speed up your query time.
I hope I could help!
VCNinc
If you have access to SQL Server, highlight your complete query in SQL server, and click + L
This will show the query execution plan. Optimize the query based on these results;
if for example you see table scans then an index may assist.
Write queries that do not use the term distinct.
Do not order results if the order is unimportant.
In your sample the complicated last set of order-by is very expensive.
Rather follow these steps:
Pull the core information into a temporary table, with 9 extra columns (type int, intially set to 0)
after populating the core data, update each of the 8 columns based on the 0 or 1 criteria
update the last column as the sum of the other 8 columns
retrieve info from the table , with only a single "order-by" based on column 9.
In my experience this approach only takes 20% of the time compared to doing the order-by in-house.

Usage of MySQL's "IF EXISTS"

Here are two statements that I'd like to work, but which return error messages:
IF EXISTS (SELECT * FROM gdata_calendars WHERE `group` = ? AND id = ?) SELECT 1 ELSE SELECT 0
and
IF ((SELECT COUNT(*) FROM gdata_calendars WHERE `group` = ? AND id = ?) > 0) SELECT 1 ELSE SELECT 0;
The question marks are there because I use parametrized, prepared, statements with PHP's PDO. However, I have also tried executing this with data manually, and it really does not work.
While I'd like to know why each of them doesn't work, I would prefer to use the first query if it can be made to work.
You cannot use IF control block OUTSIDE of functions. So that affects both of your queries.
Turn the EXISTS clause into a subquery instead within an IF function
SELECT IF( EXISTS(
SELECT *
FROM gdata_calendars
WHERE `group` = ? AND id = ?), 1, 0)
In fact, booleans are returned as 1 or 0
SELECT EXISTS(
SELECT *
FROM gdata_calendars
WHERE `group` = ? AND id = ?)
I found the example RichardTheKiwi quite informative.
Just to offer another approach if you're looking for something like IF EXISTS (SELECT 1 ..) THEN ...
-- what I might write in MSSQL
IF EXISTS (SELECT 1 FROM Table WHERE FieldValue='')
BEGIN
SELECT TableID FROM Table WHERE FieldValue=''
END
ELSE
BEGIN
INSERT INTO TABLE(FieldValue) VALUES('')
SELECT SCOPE_IDENTITY() AS TableID
END
-- rewritten for MySQL
IF (SELECT 1 = 1 FROM Table WHERE FieldValue='') THEN
BEGIN
SELECT TableID FROM Table WHERE FieldValue='';
END;
ELSE
BEGIN
INSERT INTO Table (FieldValue) VALUES('');
SELECT LAST_INSERT_ID() AS TableID;
END;
END IF;
The accepted answer works well and one can also just use the
If Exists (...) Then ... End If;
syntax in Mysql procedures (if acceptable for circumstance) and it will behave as desired/expected. Here's a link to a more thorough source/description: https://dba.stackexchange.com/questions/99120/if-exists-then-update-else-insert
One problem with the solution by #SnowyR is that it does not really behave like "If Exists" in that the (Select 1 = 1 ...) subquery could return more than one row in some circumstances and so it gives an error. I don't have permissions to respond to that answer directly so I thought I'd mention it here in case it saves someone else the trouble I experienced and so others might know that it is not an equivalent solution to MSSQLServer "if exists"!
If your table has an auto-incrementing primary key, you can use REPLACE INTO ... VALUES
SELECT #id := id FROM tableName WHERE fieldName='criteria value' LIMIT 1;
REPLACE INTO tableName(id, fieldName, col1, col2)
VALUES (#id, 'criteria value', 'value1', 'value2')
If the select statement returns NULL, then a new row is inserted.
Otherwise, if a row is found, it will update the row with key #id.
SELECT IF((
SELECT count(*) FROM gdata_calendars
WHERE `group` = ? AND id = ?)
,1,0);
For Detail explanation you can visit here