MySQL 'AND' clause does not operate as it should - mysql

I am trying to create a search engine system. In the MySQL statement, I have concatenated first name and last name and trying to get users which matches the key letters in their first name and last name. If their account_type = 0 and account_ban = 0, it will also return users with account_Type = 1 and account_ban = 1. Following is my query:
SELECT
firstname, lastname
FROM users
WHERE CONCAT(firstname,lastname) LIKE '%jb%'
OR CONCAT(firstname,lastname) LIKE '%bj%'
AND account_type = 0
AND account_ban = 0;
I have two problem here. First one that it returns users with account_type = 1 and account_ban = 1 also and second problem is that if I insert a space in the name e.g. %j b% or %b j%, it will not return anything. Please help.

It is unclear what you really want in terms of the spaces, but here is one solution:
SELECT firstname, lastname
FROM users
WHERE (CONCAT(firstname, lastname) LIKE '%j%b%' OR
CONCAT(firstname, lastname) LIKE '%b%j%'
) AND
account_type = 0 AND account_ban = 0;
This will match any character between the "b" and the "j", including no characters and a space. You can match exactly one character with '_', but that doesn't seem to be what you want. This should do the trick:
WHERE (REPLACE(CONCAT(firstname, lastname), ' ', '') LIKE '%j%b%' OR
REPLACE(CONCAT(firstname, lastname), ' ', '') LIKE '%b%j%'
) AND
account_type = 0 AND account_ban = 0;

Because AND will evaluate before OR (thats a matematically problem) you have to make parenthesis around the OR statement:
SELECT
firstname
, lastname
FROM users
WHERE
( CONCAT(firstname,lastname)
LIKE '%jb%'
OR
CONCAT(firstname,lastname)
LIKE '%bj%'
)AND
account_type = 0
AND
account_ban = 0
;

You need to add parenthesis to filter proper records.
To get account_type = 1 and account_ban = 1 you need add that condition in where clause. Try this.
SELECT firstname,
lastname
FROM users
WHERE ( Concat(firstname, lastname) LIKE '%jb%'
OR Concat(firstname, lastname) LIKE '%bj%' )
AND ( ( account_type = 0
AND account_ban = 0 )
OR ( account_type = 1
AND account_ban = 1 ) )

I think you forgot some brackets:
SELECT firstname, lastname
FROM users
WHERE (CONCAT(firstname,lastname) LIKE '%jb%'
OR CONCAT(firstname,lastname) LIKE '%bj%')
AND account_type = 0
AND account_ban = 0;
This should solve your first problem.

Use proper parenthesis will solve your problem.
Your query should look like this:
SELECT firstname, lastname
FROM users
WHERE (CONCAT(firstname,lastname) LIKE '%jb%'
OR CONCAT(firstname,lastname) LIKE '%bj%')
AND account_type = 0
AND account_ban = 0;

Related

MySQL - Return from function only if single row matches

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?
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:
RETURN (
SELECT CASE WHEN count(*) over() = 1 then ID ELSE null END
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
);
Demo Fiddle
You could use aggregation and set the condition in the HAVING clause:
CREATE FUNCTION FindUser(first_name NVARCHAR(100), last_name NVARCHAR(100))
RETURNS INT
BEGIN
RETURN (
SELECT MAX(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, '%'))
GROUP BY NULL -- you can omit this clause
HAVING COUNT(*) = 1
);
END;
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 the ID 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 not null:
WHERE (first_name IS NOT NULL OR last_name IS NOT NULL)
AND (first_name IS NULL OR FirstName LIKE CONCAT('%', first_name, '%'))
AND (last_name IS NULL OR LastName LIKE CONCAT('%', last_name, '%'))

MySQL Rows Based On Prority Values

Lets say I have a table with four columns: FirstName, LastName, Number (not a primary key) and Status. If a there are a persons with the same First name, Last name and number, but differing status (where status is a string such as "King" or "Queen" or "Jack").
I want to retrieve all the values from the table for all columns, but if there are duplicates where there are those with the same first name, last name, and number, I want to get those with status of "King", and if there a duplicates without the "King" status then get those with "Queen" and if there is no duplicates with "King" or "Queen" than only get one of those with "Jack".
Basically, the order of priority is King, Queen, and then Jack. so, I want all values from the table but if there are duplicates only include the one with the highest priority. I did some research and it appears that SQL implementations other than MYSql provide functions such as dense_rank, but I need to implement this in MYSql and I cannot find any way how.
try using session variable:
SET #row_number:=1;
SELECT
FirstName,
LastName,
Number,
Status
FROM(
SELECT
#row_number:=
CASE
WHEN #FirstName = FirstName AND #LastName = LastName AND #Number = Number
THEN #row_number + 1
ELSE 1
END AS num,
#FirstName := FirstName as FirstName,
#LastName := LastName as LastName,
#Number := Number as Number,
Status
FROM
t1
ORDER BY
FirstName,
lastName,
Number,
CASE
WHEN STATUS = 'King' THEN '1'
WHEN STATUS = 'Queen' THEN '2'
WHEN STATUS = 'Jack' THEN '3'
END
) as ttt
WHERE num = 1;
One method of doing this involves union all:
select t.*
from t
where t.status = 'King'
union all
select t.*
from t
where t.status = 'Queen' and
not exists (select 1 from t t2 where t2.name = t.name andt2.status in ('King'))
union all
select t.*
from t
where t.status = 'Jack' and
not exists (select 1 from t t2 where t2.name = t.name and t2.status in ('King', 'Queen'));
Another method uses a correlated subquery:
select t.*
from t
where (name, field(t.status, 'King', 'Queen', 'Jack')) in
(select t2.name, max(field(t2.status, 'King', 'Queen', 'Jack'))
from t t2
where t2.status in ('King', 'Queen', 'Jack')
group by t2.name
);

SQL Subquery IN SELECT [Symfony3]

I Have Table Actions with the following fields :
idAction
Cloture
Date
I'd Like to loop through and display a list of all idAction In my DB + idAction with Cloture = 0 Group By the same Date (both of them).
I tried below method. But it doesn't work. Can anyone help me?
$query = $this->getEntityManager()->createQuery(
'SELECT COUNT(a.idAction) AS nmbreAction , week(a.dateOuverture) AS week,( SELECT COUNT(c.idAction) , week(c.dateOuverture) FROM ActionActionBundle:Action c
WHERE c.cloture = 0 ) AS nmbreRetard FROM ActionActionBundle:Action a
GROUP BY week');
Mmm, you question lacks a lot of information. Is this what you need?
SELECT COUNT(a.idAction) AS nmbreAction ,
week(a.dateOuverture) AS week,
(SELECT COUNT(c.idAction)
FROM ActionActionBundle:Action c
WHERE c.cloture = 0
and week(c.dateOuverture) = week(a.dateOuverture)) AS nmbreRetard
FROM ActionActionBundle:Action a
GROUP BY week(a.dateOuverture)
You can't select more than 1 column in a sub query\correlated query in the select list, which was probably showed to you in the error message.
EDIT: Better of do that:
SELECT COUNT(a.idAction) AS nmbreAction ,
week(a.dateOuverture) AS week,
COUNT(CASE WHEN a.cloture = 0 THEN 1 END) as nmbreRetard
FROM ActionActionBundle:Action a
GROUP BY week(a.dateOuverture)

Combining 2 Tables with an OUTER JOIN on Another Table

I need to combine 2 tables that may or may not have the data in them, but than I need a full outer join where the last table (if has content where IsActive = 1) gets shown that data, instead of the combined first 2 tables.
Currently have this:
( SELECT qp.ItemName AS name
, qp.TimeAdded AS created
, '' AS effective
, qp.VendorName AS supplier
, qp.Source AS source
, qp.VendorType AS type
, qp.Price AS cost
, '' AS price
, '' AS markup
, '' AS customer
, '' AS customerListID
, qp.VendorListID AS vendorListID
, '' AS itemListID
FROM wp_quantum_purchases AS qp
WHERE qp.IsActive = 1 AND
NOT EXISTS ( SELECT 1
FROM wp_hunter_quote_parts AS hqp
WHERE qp.ItemName = hqp.ItemName AND
hqp.IsActive = 1 ))
UNION ALL
( SELECT qs.ItemName AS name
, qs.TimeAdded AS created
, qs.SalesDate AS effective
, '' AS supplier
, qs.Source AS source
, '' AS type
, '' AS cost
, qs.Price AS price
, '' AS markup
, qs.CustomerName AS customer
, qs.CustomerListID AS customerListID
, '' AS vendorListID
, '' AS itemListID
FROM wp_quantum_sales AS qs
WHERE qs.IsActive = 1 AND
NOT EXISTS ( SELECT 1
FROM wp_hunter_quote_parts AS hqp
WHERE qs.ItemName = hqp.ItemName AND
hqp.IsActive = 1 ))
UNION ALL
( SELECT hqp.ItemName AS name
, hq.Quote_Date AS created
, hqp.SalesDate AS effective
, hqp.VendorName AS supplier
, hqp.Source AS source
, hqp.VendorType AS type
, hqp.Cost AS cost
, hqp.Price AS price
, CAST(( ( ( CAST(hqp.Price AS DECIMAL(10, 2)) - CAST(hqp.Cost AS DECIMAL(10, 2)) ) / CAST(hqp.Cost AS DECIMAL(10, 2)) ) * 100 ) AS DECIMAL(10, 2)) AS markup
, IFNULL(hq.Customer_FullName, 'N/A') AS customer
, hq.Customer_ListID AS customerListID
, hqp.VendorListID AS vendorListID
, hqp.Item_ListID AS itemListID
FROM wp_hunter_quote_parts AS hqp
LEFT JOIN wp_hunter_quotes AS hq
ON ( hq.id = hqp.QuoteID )
WHERE hqp.IsActive = 1)
ORDER BY NAME ASC;
But this is duplicating the data in 1st and 2nd tables and shows the data twice. I need the data from 1st and 2nd tables to be combined as 1 (if exists), but to prioritize the last table (wp_hunter_quote_parts) in here as the content to show from, if IsActive = 1 exists in the last table (wp_hunter_quote_parts). However, if IsActive = 1 does not exist in wp_hunter_quote_parts for ItemName than I would like to combine both wp_quantum_purchases and wp_quantum_sales as if it were 1 row.
Can not do a LEFT JOIN since data could exist in wp_quantum_purchases, but not in wp_quantum_sales OR data could exist in wp_quantum_sales and not in wp_quantum_purchases, OR data could not exist in either of these, and only exist in wp_hunter_quote_parts as well as data might not even exist in wp_hunter_quote_parts.
So, basically, if ItemName exists in wp_quantum_purchases AND IsActive = 1 AND wp_hunter_quote_parts does not have ItemName in table, get purchase data from wp_quantum_purchases, else if ItemName exists in wp_hunter_quote_parts get data from hunter_quote_parts instead.
If ItemName exists in wp_quantum_sales AND IsActive = 1 AND wp_hunter_quote_parts does not have ItemName in table, get sales data from wp_quantum_sales, else if ItemName exists in wp_hunter_quote_parts get data from hunter_quote_parts instead.
How can I combine first and second table, than do an outer join on it with another table?
Another Attempt here:
(SELECT IFNULL(qp.ItemName, qs.ItemName) AS name, IFNULL(qp.TimeAdded, qs.TimeAdded) AS created, qs.SalesDate AS effective, qp.VendorName AS supplier, qp.Source AS source, qp.VendorType AS type, qp.Price AS cost, qs.Price AS price, CAST((((CAST(qs.Price AS DECIMAL(10,2)) - CAST(qp.Price AS DECIMAL(10,2))) / CAST(qp.Price AS DECIMAL(10,2))) * 100) AS DECIMAL(10,2)) AS markup, qs.CustomerName AS customer, qs.CustomerListID AS customerListID, qp.VendorListID AS vendorListID, '' AS itemListID
FROM wp_quantum_purchases AS qp, wp_quantum_sales AS qs
WHERE (qp.IsActive = 1 OR qs.IsActive = 1)
AND NOT EXISTS (
SELECT 1
FROM wp_hunter_quote_parts AS hqp
WHERE (qp.ItemName = hqp.ItemName || qs.ItemName = hqp.ItemName) AND hqp.IsActive = 1
)
)
UNION ALL
(SELECT hqp.ItemName AS name, hq.Quote_Date AS created, hqp.SalesDate AS effective, hqp.VendorName AS supplier, hqp.Source AS source, hqp.VendorType AS type, hqp.Cost AS cost, hqp.Price AS price, CAST((((CAST(hqp.Price AS DECIMAL(10,2)) - CAST(hqp.Cost AS DECIMAL(10,2))) / CAST(hqp.Cost AS DECIMAL(10,2))) * 100) AS DECIMAL(10,2)) AS markup, IFNULL(hq.Customer_FullName, 'N/A') AS customer, hq.Customer_ListID AS customerListID, hqp.VendorListID AS vendorListID, hqp.Item_ListID AS itemListID
FROM wp_hunter_quote_parts AS hqp
LEFT JOIN wp_hunter_quotes AS hq ON (hq.id = hqp.QuoteID)
WHERE (hqp.IsActive = 1))
ORDER BY name ASC
Figured this one would work, but seems that it just keeps going and going and going, and doesn't seem to ever finish the query. No errors that I can see, but doesn't finish ever... And these tables are very small, that is odd...
I may not be understanding your question fully, but you could create a view of the first two tables and then do an outer join with the third table.

Where mistake in sql LIKE clause?

I try to count all items from another table with this select:
SELECT id, name, (SELECT count(*)
FROM prekes_main
WHERE prekes_main.pristKaina = 1
and prekes_main.pg_kodas LIKE 'grupes_main.pg_kodas%') as pristKaina
FROM grupes_main
WHERE grupes_main.level = 1
and grupes_main.name <> ''
In LIKE clause I want automatically get selected grupes_main column pg_kodas, but in this query it always returns 0, where is mistake in LIKE function? thx
SELECT id, name,
(
SELECT COUNT(*)
FROM prekes_main
WHERE prekes_main.pristKaina = 1
AND prekes_main.pg_kodas LIKE CONCAT(grupes_main.pg_kodas, '%')
) pristKaina
FROM grupes_main
WHERE grupes_main.level = 1
AND grupes_main.name <> ''