MySQL: Getting multiple rows based on the value of one row - mysql

My MySQL table looks like the following:
I would like to get results where all the followings are satisfied:
form_id is 1
sort by lead_id high to low
field_number 9 for that lead_id must be 111
In summary, my query should return 9 rows. Is that possible?
Here is my attempt so far, but I really want to avoid using GROUP_CONCAT if at all possible to get a cleaner result set.
SELECT lead_id, GROUP_CONCAT(field_number, "|", value SEPARATOR "----") AS `values`
FROM lead_detail
WHERE form_id = 1
GROUP BY lead_id
ORDER BY lead_id DESC

If you are meaning that you want to include only rows for a lead_id if there exists a row for that lead_id with field_number=9 and value=111. And otherwise, all rows for that lead_id should be excluded...
You could do something like this:
SELECT d.id
, d.lead_id
, d.form_id
, d.field_number
, d.value
FROM ( SELECT e.lead_id
FROM lead_detail e
WHERE e.form_id = 1
AND e.field_number = 9
AND e.value = 111
GROUP BY e.lead_id
) f
JOIN lead_detail d
ON d.lead_id = f.lead_id
AND d.form_id = 1
ORDER BY d.lead_id DESC
The inline view (aliased as f) returns a distinct list of lead_id that meet specific criteria. We can reference the result from that like it were a table, and use that in a JOIN operation, to return "matching" rows from the lead_detail table. (If there's no row with value=111, field_number=9, and form_id=1 for a particular lead_id, then the inline view won't return that lead_id in the list.)
As another alternative, we could use an EXISTS predicate with a correlated subquery, but this may not perform as well:
SELECT d.id
, d.lead_id
, d.form_id
, d.field_number
, d.value
FROM lead_detail d
WHERE d.form_id = 1
AND EXISTS
( SELECT 1
FROM lead_detail e
WHERE e.form_id = 1
AND e.field_number = 9
AND e.value = 111
AND e.lead_id = d.lead_id
)
ORDER BY d.lead_id DESC
That essentially says, for every row from lead_detail run the subquery following the EXIST keyword... if the subquery returns one (or more) rows, then the EXISTS predicate returns TRUE, otherwise it returns FALSE. That subquery is "correlated" to the outer query, by a predicate in the WHERE clause, matching the lead_id to the value of lead_id from the row from the outer query.

The short answer is "not easily." There are a couple of ways to do this that aren't super straightforward -- probably the easiest is with a sub-query (the exact format will change depending on the exact format of your table, etc.)
SELECT lead_id, GROUP_CONCAT(field_number, "|", value SEPARATOR "----") AS `values`
FROM lead_detail
WHERE `form_id` = 1
AND `lead_id` = (SELECT `lead_id` FROM `lead_detail` WHERE `field_number` = 9 AND `value` = 111 AND `form_id` = 1) /*This is the subquery*/
GROUP BY lead_id
ORDER BY lead_id DESC
Other options include running two separate queries, one to pull the form_id (or form_ids) in question, the other to pull all data for that form_id (or those form_ids).

Related

MySQL - if count in select subquery is null change to 0

I have following query:
select `jobs`.*,
(SELECT COUNT(user_jobs_application.id) as count FROM user_jobs_application
join job_shifts on job_shifts.id = user_jobs_application.job_shift_id
where job_shifts.jobs_id = jobs.id
and user_jobs_application.status = 1
group by user_jobs_application.users_id
) as entries
from `jobs` where `jobs`.`deleted_at` is null order by `id` desc limit 25 offset 0
The subquery in select will give null instead of 0. Can I change this so if the value is null it will show 0?
Removing the group by clause from the subquery should be sufficient. It is not needed anyway, since it groups on the column you are filtering on (and it it was needed, then I would mean the subquery may return more than one row, which would generate a runtime error).
select
j.*,
(
select count(*) as count
from user_jobs_application uja
join job_shifts js on js.id = uja.job_shift_id
where js.jobs_id = j.id and uja.status = 1
) as entries
from jobs j
where j.deleted_at is null
order by id desc limit 25 offset 0
Other changes to your query:
presumably, user_jobs_application(id) is not nullable; if so, count(*) is good enough, and is more efficient than count(user_jobs_application.id)
table aliases make the query easier to read and write

How efficiently check record exist more than 2 times in table using sub-query?

I have a query like this . I have compound index for CC.key1,CC.key2.
I am executing this in a big database
Select * from CC where
( (
(select count(*) from Service s
where CC.key1=s.sr2 and CC.key2=s.sr1) > 2
AND
CC.key3='new'
)
OR
(
(select count(*) from Service s
where CC.key1=s.sr2 and CC.key2=s.sr1) <= 2
)
)
limit 10000;
I tried to make it as inner join , but its getting slower . How can i optimize this query ?
The trick here is being able to articulate a query for the problem:
SELECT *
FROM CC t1
INNER JOIN
(
SELECT cc.key1, cc.key2
FROM CC cc
LEFT JOIN Service s
ON cc.key1 = s.sr2 AND
cc.key2 = s.sr1
GROUP BY cc.key1, cc.key2
HAVING COUNT(*) <= 2 OR
SUM(CASE WHEN cc.key = 'new' THEN 1 ELSE 0 END) > 2
) t2
ON t1.key1 = t2.key1 AND
t1.key2 = t2.key2
Explanation:
Your original two subqueries would only add to the count if a given record in CC, with a given key1 and key2 value, matched to a corresponding record in the Service table. The strategy behind my inner query is to use GROUP BY to count the number of times that this happens, and use this instead of your subqueries. The first count condition is your bottom subquery, and the second one is the top.
The inner query finds all key1, key2 pairs in CC corresponding to records which should be retained. And recognize that these two columns are the only criteria in your original query for determining whether a record from CC gets retained. Then, this inner query can be inner joined to CC again to get your final result set.
In terms of performance, even this answer could leave something to be desired, but it should be better than a massive correlated subquery, which is what you had.
Basically get the Columns that must not have a duplicate then join them together. Example:
select *
FROM Table_X A
WHERE exists (SELECT 1
FROM Table_X B
WHERE 1=1
and a.SHOULD_BE_UNIQUE = b.SHOULD_BE_UNIQUE
and a.SHOULD_BE_UNIQUE2 = b.SHOULD_BE_UNIQUE2
/* excluded because these columns are null or can be Duplicated*/
--and a.GENERIC_COLUMN = b.GENERIC_COLUMN
--and a.GENERIC_COLUMN2 = b.GENERIC_COLUMN2
--and a.NULL_COLUMN = b.NULL_COLUMN
--and a.NULL_COLUMN2 = b.NULL_COLUMN2
and b.rowid > a.ROWID);
Where SHOULD_BE_UNIQUE and SHOULD_BE_UNIQUE2 are columns that shouldn't be repeated and have unique columns and the GENERIC_COLUMN and NULL_COLUMNS can be ignored so just leave them out of the query.
Been using this approach when we have issues in Duplicate Records.
With the limited information you've given us, this could be a rewrite using 'simplified' logic:
SEELCT *
FROM CC NATURAL JOIN
( SELECT key1, key2, COUNT(*) AS tally
FROM Service
GROUP
BY key1, key2 ) AS t
WHERE key3 = 'new' OR tally <= 2;
Not sure whether it will perform better but might give you some ideas of what to try next?

MySQL return 'empty result' even with coalesce

I have some trouble with MySQL.
Here is the query I use:
SELECT
COALESCE(SUM(`a`.`battles`), 0) AS `battles`
FROM
`account_stats` AS `a`
WHERE
`a`.`account_id` = 12345
GROUP BY
`a`.`account_id`
The Table account_stats is not empty, but has no row with account_id = 12345.
I want that MySQL returns 0 battles instead of Empty set. But even with COALSECE or IFNULL it returns Empty set.
When I remove the GROUP BY everything works fine, but I need it to calculate the SUM of battles.
Is there a way to workaround this problem?
If you only want information on one account, you can use conditional aggregation if you want the query to return a row with the value of 0:
SELECT SUM(CASE WHEN a.account_id = 12345 THEN a.battles ELSE 0 END) as battles
FROM account_stats a;
If the table is not empty, then you don't need coalesce().
If you have an index on account_id and the table is big, the following would probably be more efficient because the subquery would use the index and the rest of the query would be manipulating a single row:
SELECT x.account_id, COALESCE(SUM(a.battles), 0) as battles
FROM (SELECT 12345 as account_id
) x LEFT JOIN
(SELECT a.account_id, SUM(a.battles) as battles
FROM account_stats a
WHERE a.account_id = 12345
) a
ON x.account_id = a.account_id;

Query output differs from the expected output

Below query is doing what I need:
SELECT assign.from_uid, assign.aid, assign.message, curriculum.asset,
curriculum.title, curriculum.description
FROM assignment assign
INNER JOIN curriculum_topics_assets curriculum
ON assign.nid = curriculum.asset
WHERE assign.to_uid = 13 AND assign.status = 1
GROUP BY assign.from_uid, assign.to_uid, assign.nid
ORDER BY assign.created DESC
Now I need to get the total count of rows of the result. For example if it is displaying 5 rows the o/p should be like My expected o/p. The query I tried is given below.
SELECT count(description) FROM assignment assign
INNER JOIN curriculum_topics_assets curriculum ON assign.nid = curriculum.asset
WHERE assign.to_uid = 13 AND assign.status = 1
GROUP BY assign.from_uid, assign.to_uid, assign.nid
ORDER BY assign.created DESC
My expected o/p:
count(*)
---------
5
My current o/p:
count(*)
---------
6
2
5
6
6
The easiest solution would be to
place your initial GROUP BY query in a subselect
select the amount of rows retrieved from this subselect
SQL Statement
SELECT COUNT(*)
FROM (
SELECT assign.from_uid
FROM assignment assign
INNER JOIN curriculum_topics_assets curriculum ON assign.nid = curriculum.asset
WHERE assign.to_uid = 13
AND assign.status = 1
GROUP BY
assign.from_uid
, assign.to_uid
, assign.nid
) q
Edit - why doesn't the original query return the results required
It did already prepared what was needed to get the correct result
Your query without grouping returns a resultset of 25 records (6+2+5+6+6)
From these 25 records, you have 5 unique combinations of from_uid, to_uid, nid
Now you don't want to count how many records each combination has (as you did in your example) but how many unique (distinct anyone?) combinations there are.
One solution to this is the subselect I presented but following equivalent statement using a DISTINCT clause might be more comprehensive.
SELECT COUNT(*)
FROM (
SELECT DISTINCT assign.from_uid
, assign.to_uid
, assign.nid
FROM assignment assign
INNER JOIN curriculum_topics_assets curriculum ON assign.nid = curriculum.asset
WHERE assign.to_uid = 13
AND assign.status = 1
) q
Note that my personal preference goes to the GROUP BY solution.
To get the number of rows for a query do:
SELECT COUNT(*) as RowCount FROM (--insert other query here--) s
In you example:
SELECT COUNT(*) as RowCount FROM (SELECT a.from_uid
FROM assignment a
INNER JOIN curriculum_topics_assets c ON a.nid = c.asset
WHERE a.to_uid = 13
AND a.status = 1
GROUP BY a.from_uid, a.to_uid, a.nid
) s
Note that I the dropped the stuff that has no effect on the number of rows to make the query run slightly faster.
You should use COUNT(*) instead of count(description). Look at: http://www.mysqlperformanceblog.com/2007/04/10/count-vs-countcol/

Select value that is not "*username*"

My problem is this:
I need to select values from "groups" table that do not contain specific "user_id".
So I execute this:
SELECT DISTINCT group.active, hide_group.user_id, group.group_id, hide_group.hide, group.desc AS group_desc
FROM group
LEFT JOIN hide_group ON hide_group.group_id = group.group_id
WHERE (
hide_group.user_id != 'testuser'
OR hide_group.user_id IS NULL
AND (
hide != 'true'
OR hide IS NULL
)
)
AND active =1
ORDER BY `group`.`group_id` ASC
LIMIT 0 , 500
But now lets say we have such records in my 'group_hide' table:
[group_hide]
(user_id, group_id, hide)
john, ABC, true;
joe, ZZZ, true;
mark, ABC, true;
So now when I do my query, mark still sees ABC group, because the condition is true and valid because user_id = john and we therefor take the ABC value, even when it is hidden for mark.
I have tried changing this query several times, but I can't figure out this simple problem.
I suggest using a subquery:
SELECT *
FROM group
WHERE group_id NOT IN (
SELECT group_id
FROM group_hide
WHERE user_id = 'testuser'
AND hide = true)
AND active =1
ORDER BY `group`.`group_id` ASC
LIMIT 0 , 500
You can use Having to apply conditions on the JOIN results. I would also use GROUP BY rather than DISTINCT.
I am not sure I completely follow your WHERE condition (I think you have some mixup with the parenthesis), but here's a version which may get you the result you need:
SELECT DISTINCT group.active, hide_group.user_id, group.group_id, hide_group.hide, group.desc AS group_desc
FROM group
LEFT JOIN hide_group ON hide_group.group_id = group.group_id
WHERE active =1
HAVING (hide_group.user_id != 'testuser'
OR hide_group.user_id IS NULL)
AND
(hide != 'true'
OR hide IS NULL)
ORDER BY `group`.`group_id` ASC
LIMIT 0 , 500
If you post the tables structure, some sample data and a sample wanted output, you'll get better responses with working queries...