I have a MySQL table of users as follows:
CREATE TABLE `users` (
`ID` INT NOT NULL, -- NOTE: in practice, I'm using BINARY(16) and
-- the UUID()-function to create user IDs.
-- 'INT' is only a simplification for this
-- stackoverflow question.
`FirstName` NVARCHAR(100) NOT NULL,
`LastName` NVARCHAR(100) NOT NULL,
-- ...
PRIMARY KEY (`ID`)
);
INSERT INTO `users` (`ID`, `FirstName`, `LastName`)
VALUES (0, 'Albus', 'Dumbledore'),
(1, 'Lord', 'Voldemort'),
(2, 'Harry', 'Potter'),
(3, 'Hermione', 'Granger');
I’d like to create a user-defined function which returns the ID
of the row matching a FirstName
and LastName
combination if (and only if) the results are unique (i.e. only one row matches the query):
CREATE FUNCTION `FindUser`(`first_name` NVARCHAR(100), `last_name` NVARCHAR(100)
RETURNS INT
BEGIN
RETURN (SELECT `ID`
FROM `users`
WHERE ((first_name is NULL) OR (`FirstName` LIKE CONCAT('%', first_name, '%')))
AND ((last_name Is NULL) OR (`LastName` LIKE CONCAT('%', last_name, '%')))
LIMIT 1);
END
This works as expected on the following examples:
SELECT `FindUser`(NULL, 'potter');
-- | ID |
-- |----|
-- | 2 |
SELECT `FindUser`('obama', NULL);
-- | ID |
-- |----|
However, this does not work on SELECT FindUser(NULL, 'or');
, as the token 'or'
could match 0 | Albus | Dumbledore
and 1 | Lord | Voldemort
.
I tried the following:
SET @cnt = 0;
SET @id = NULL;
SELECT @id = u.id, @cnt = COUNT(id)
FROM users u
WHERE ...; -- same conditions as before
RETURN IF(@cnt = 1, @id, NULL);
However, that does not work, as @id
and @cnt
will always be overwritten by the last line.
The alternative would be to perform two queries, but that is inefficient.
How could I solve the problem most efficiently?
2
Answers
Providing you’re using a MySql version that supports window functions a simple modification you can make is to conditionally count the number of rows:
Demo Fiddle
You could use aggregation and set the condition in the
HAVING
clause:See the demo.
I suspect that for the edge case where there is only 1 row in the table and the parameters that you pass for the function are both
null
you don’t want theID
of that row returned.For this case you should add one more condition in the
WHERE
clause to make sure that at least one of the parameters is notnull
: