Add an AND condition only IF a certain expression is true - mysql

I want to add an AND condition only if an expression is true, otherwise the AND condition should be ignored.
I'm supposed to group concat the phone numbers if they have IsConcat set to true, otherwise I need to select the first one which doesn't have IsConcat set to true.
What I've tried so far:
SELECT IF(p.IsConcat = 1, GROUP_CONCAT(p.PhoneNumber), p.PhoneNumber) AS 'PhoneNumber'
FROM phones p
WHERE p.IdStudent = _idStudent
AND IF(p.IsConcat = 1, TRUE, '') -- only check if IsConcat = 1 otherwise i don't need the AND statement
LIMIT 1
I saw somewhere online that TRUE is being used in if statements, thought it would work but it didn't.
I have some mock data: Two students, the student with Id = 1 has 5 phones and only 3 of them have IsConcat = 1 -> the output works good since it does a group concat and shows only those 3 numbers.
The student with Id = 2 has two phone numbers and both have IsConcat = 0, which means I need to select the first one, without checking if IsConcat = 1, that's why I added LIMIT 1. In that case it outputs NULL.
The expected output if the phones table has any numbers for the appropriate student with IsConcat = 1 should group_concat only them.
If the phones table has no phone numbers with IsConcat = 1 for the appropriate student, it should output the first phone number.

I found a solution, thank you #Akina for giving me the idea to use SUM().
If SUM(p.IsConcat) > 1, it goes to a subquery and does group_concat to ones that have IsConcat = 1.
This is the final solution
SELECT IF(SUM(p.IsConcat) > 0, (SELECT GROUP_CONCAT(pp.PhoneNumber)
FROM phones pp
WHERE pp.IdStudent = _idStudent
AND pp.IsConcat = 1
LIMIT 1
), p.PhoneNumber)
FROM phones p
WHERE p.IdStudent = _idStudent
LIMIT 1

Related

mysql select counts by if else condition

i have a table named cq500_all(to record diffrent doctor feedback)
now i want know counts when condition status is
field dr_1_finish and field dr_2_finish value is all fill 1
and
when field dr_1 different dr_2 (like dr_1=1 and dr_2=0,or dr_1=0 and dr_2=1 )
cause i want to know two doctors feedback counts (when different doctor's feedback on jpg)
for example image show CQ500-CT-1_36_08.jpg and CQ500-CT-1_36_09.jpg is match my select counts
it will be two (select counts result)
how to make the query on mysql?
You can count as
select count(*) as total
from cq500_all
where dr_1_finish = 1 and dr_2_finish = 1 and dr_1 != dr_2
You will got result in total
Pretty much just the way you've described it:
select *
from cq500_all
where dr_1_finish = 1 and dr_2_finish = 1
and dr_1 != dr_2
or (if dr_1 or dr_2 might not be just 0 and 1):
select *
from cq500_all
where dr_1_finish = 1 and dr_2_finish = 1
and ((dr_1 = 1 and dr_2 = 0) or (dr_1 = 0 and dr_2 = 1))

MySQL updating duplicate IDs based on match and no match criteria all in one table

Hopefully I can explain this clearly. I have a table that has what need to be unique IDs for people within a group. The IDs are generated using first 3 letters of the first name and date of birth. Normally, with smaller groups (less than 500) this works fine. However in large groups we do hit upon some duplicates. We'd then just append a -1, -2, -3 etc. to any duplicate IDs. For example:
ID GROUP UID FIRST_NAME
1 123456 ALE19900123 ALEXIS
2 123456 ALE19900123 ALEXANDER
3 123456 ALE19900123 ALEJANDRO
4 789789 ALE19900123 ALEX
What I'd like to do is for ID 2 and 3 append a -1 and -2 respectively to their UID field so that 1,2 and 3 are now unique (GROUP + UID). ID 4 would be ignored because the GROUP is different
I've started with something like this:
UPDATE table A
JOIN table B
ON B.GROUP = A.GROUP
AND B.UID = A.UID
AND B.FIRST_NAME <> A.FIRST_NAME
AND B.ID < A.ID
SET A.duplicate_record = 1;
That should set the duplicate_record field = 1 for IDs 2 and 3. But then I still need to append a -1, -2, -3 etc. to those UIDs and I'm not sure how to do that. Maybe instead of just setting a flag = 1 for duplicate I should set the count of records that are duplicates?
If group, UID tuple is unique (and it should be), why not insert ignore the first one (without any value appended), check for how many rows were affected by SELECT ROW_COUNT();, and if that is zero, append -1? If you put it in a for cycle (pseudocode):
while i < 1000 do
insert ignore into people (group, uid, first_name) values (123456, concat(their_uid, "-", i), first name);
if ((select row_count();) == 1):
break;
i=i+1;
end while;

Is it possible to 'concat' two different COUNT(column) WHERE column can have two differen values and different total?

I come with this problem in a kinda complex query to me. Let me be more clear with an example.
If I have a banner table which stores all the banners on a site and I have another table called banner_statistics which stores a record for each banner event in a particular period of time (the event can be 'I', which means an impression of the banner and 'C', which means a click on a banner).
If I run this query:
-- Count number of impressions of the banner with id = 1
SELECT
ban_id,
ban_url,
COUNT(bansta_event) as num_impressions
FROM banner
LEFT JOIN banner_statistics
ON bansta_ban_id = ban_id
AND bansta_event = 'I'
WHERE ban_id = 1
I get:
ban_id ban_url num_impressions
1 http://www.cocacola.com 7
It says that the banner with id = 1 which is a cocacola banner had a total of 7 impressions.
If I run the same query with the 'C' (click event on that banner):
-- Count number of impressions of the banner with id = 1
SELECT
ban_id,
ban_url,
COUNT(bansta_event) as num_impressions
FROM banner
LEFT JOIN banner_statistics
ON bansta_ban_id = ban_id
AND bansta_event = 'C'
WHERE ban_id = 1
I get:
ban_id ban_url num_clicks
1 http://www.cocacola.com 1
It says that this banner had only one click
Now how can I merge those results into a single query in order to obtain this result:
ban_id ban_url num_impressions num_clicks
1 http://www.cocacola.com 7 1
Is there a way to do it? It seems like a CONCAT but it's not cause it is the result of another query with a COUNT on the same column but where the condition on what to count on that column is different...
How can I achieve this in MySQL? I have found that there is GROUP_CONCAT but it can't receive the result of a SELECT query as param, and I don't think that it can work in my case...
Try this:
SELECT
ban_id,
ban_url,
IFNULL(SUM(bansta_event = 'C'), 0) as num_impressions,
IFNULL(SUM(bansta_event = 'I'), 0) as num_clicks
FROM banner
LEFT JOIN banner_statistics
ON bansta_ban_id = ban_id
AND bansta_event IN ('I', 'C')
WHERE ban_id = 1
In MySQL, comparison operators return 1 when they match, 0 when they don't. So summing them gets the count of rows that match that criteria. The IFNULL is needed in case there are no rows that match that the criteria, so we get 0 instead of NULL in the results.

how can I tell if the last x rows of 'state' = 1

I need help with a SQL query.
I have a table with a 'state' column. 0 means closed and 1 means opened.
Different users want to be notified after there have been x consecutive 1 events.
With an SQL query, how can I tell if the last x rows of 'state' = 1?
If, for example, you want to check if the last 5 consecutive rows have a state equals to 1, then here's you could probably do it :
SELECT IF(SUM(x.state) = 5, 1, 0) AS is_consecutive
FROM (
SELECT state
FROM table
WHERE Processor = 3
ORDER BY Status_datetime DESC
LIMIT 5
) as x
If is_consecutive = 1, then, yes, there is 5 last consecutive rows with state = 1.
Edit : As suggested in the comments, you'll have to use ORDER BY in your query, to get the last nth rows.
And for more accuracy, since you have a timestamp column, you should use Status_datetime to order the rows.
You should be able to use something like this (replace the number in the HAVING with the value of x you want to check for):
SELECT Processor, OpenCount FROM
(
SELECT TOP 10 Processor, DateTime, Sum(Status) AS OpenCount
FROM YourTable
WHERE Processor = 3
ORDER BY DateTime DESC
) HAVING OpenCount >= 10

Find rows that have 3 out of 5 fields in common - how to speed up query?

The query below works great but is slow. In a table of about 7500 rows it takes about 30s to execute. How could I speed it up?
The goal is to find "almost duplicate" rows within the same table. When there are 3 out of 5 fields matching we have a hit.
SELECT 
originalTable.id,
originalTable.lastname,
originalTable.firstname,
originalTable.address,
originalTable.city,
originalTable.email
FROM
address as originalTable,
address as compareTable
WHERE
# do not find the same record
originalTable.id != compareTable.id and
# at least 3 out of those 5 should match
(originalTable.firstname = compareTable.firstname) +
(originalTable.lastname = compareTable.lastname)  +
(originalTable.address = compareTable.address and originalTable.address != '')  +
(originalTable.city = compareTable.city and originalTable.city != '')  +
(originalTable.email = compareTable.email and originalTable.email != '')
>= 3
GROUP BY
originalTable.id
ORDER BY
originalTable.lastname asc,
originalTable.firstname asc,
originalTable.city asc
Thanks for any optimization hints.
A Cartesian product is required here, that's true. I came up with the following solution:
CREATE TABLE address_dups(INDEX (is_duplicate)) ENGINE=MEMORY
SELECT
originalTable.id,
compareTable.id,
(
(originalTable.firstname = compareTable.firstname) +
(originalTable.lastname = compareTable.lastname) +
(originalTable.address = compareTable.address and originalTable.address != '') +
(originalTable.city = compareTable.city and originalTable.city != '') +
(originalTable.email = compareTable.email and originalTable.email != '')
>= 3
) AS is_duplicate
FROM
address as originalTable,
address as compareTable
WHERE originalTable.id != compareTable.id;
SELECT * FROM address_dups WHERE is_duplicate = 1;
This will give you for each row ID the fuzzy duplicate row IDs you request as well.
Your comparison as already noted will require the Cartesian... but only a PARTIAL. Since you are requiring a value in both your first and last name fields, I would have an index on AT LEAST the last name, first name. Then, add a WHERE clause to your condition on partial of last name only... say first 2-3 characters. This way, it will only Cartesian against those in the same name prefix vs the rest. No sense in comparing a "Bill Jones" to "Tonya Smith". However, you might be interested in "Bill Jones" vs "William Jones" at common address, city and/or email. Consider the following name portions for Cartesian comparison.
(names fictitious for sample)
ID Last First
1 Adams Brian
2 Adams Marsha
3 Andrews Jeff
4 Brown Steve
5 Johns Dave
6 Johnson Bill
7 Johnson William
Both the "Adams" would be compared if you only qualified the left 3 of each last name in your where clause. "Andrews" and "Brown" would have no comparison match to anyone. Then, the 3 starting with "Joh" would be Cartesian tested...
Now, add in one MORE to your where clause... Since you have an ID column, make sure that too is part of your where clause. Where the ID of the second table is ALWAYS greater than the one you are on. Ex: When comparing the "Adams" names. You will already know if the ID1 was compared to ID2 as a duplicate or not (not, in this case), so why go backwards and re-compare ID2 to ID1.
So, this sample of 7 records will result in comparisons of
1-2
2-no more to compare against
3-no more to compare against
4-no more to compare against
5-6
5-7
6-7
7-no more to compare against
So a final where would be something like (including the ID that was a close match as basis to look back to.. You could even get all the columns as "MatchFirstName, MatchLastName, MatchCity, etc" just for previewing purposes... )
SELECT
originalTable.id,
originalTable.lastname,
originalTable.firstname,
originalTable.address,
originalTable.city,
originalTable.email,
compareTable.ID as MatchID
FROM
address as originalTable,
address as compareTable
WHERE
originalTable.ID < CompareTable.ID
AND left( originalTable.LastName, 3 ) = left( CompareTable.LastName, 3 )
AND (originalTable.firstname = compareTable.firstname)
+ (originalTable.lastname = compareTable.lastname)
+ (originalTable.address = compareTable.address and originalTable.address != '')
+ (originalTable.city = compareTable.city and originalTable.city != '')
+ (originalTable.email = compareTable.email and originalTable.email != '') >= 3