MySQL optimization - prevent subqueries - mysql

Is it possible for me to change this query so I can do like COUNT(WHERE type_id = 2) AS success, COUNT(WHERE type_id = 1) AS error to prevent using subqueries?
SELECT
(SELECT COUNT(id) FROM log AS success_log WHERE type_id = 2 AND site_id = site.id AND DAYOFYEAR(success_log.created)) AS success,
(SELECT COUNT(id) FROM log AS success_log WHERE type_id = 1 AND site_id = site.id AND DAYOFYEAR(success_log.created)) AS error,
(SELECT COUNT(id) FROM log AS success_log WHERE site_id = site.id AND DAYOFYEAR(success_log.created)) AS total,
DATE_FORMAT(log.created, "%m-%d-%y") AS `day`
FROM log
WHERE site_id = ?
GROUP BY DAYOFYEAR(created)`

You can use single count with GroupBy TypeId and insert them in the temp table and then select the way you want it.

since they were all querying from the log table for the same site, you can just run through the table ONCE and just apply and IMMEDIATE IF() test inside a sum.... If the record is of a given type, count as 1, otherwise 0... Gets a sum of each of type 1 or type 2 and simple count(*) gets the overall count for the day in question.
SELECT
DATE_FORMAT(log.created, "%m-%d-%y") AS `day`,
sum( if( type_id = 2, 1, 0 )) as NumberSuccess,
sum( if( type_id = 1, 1, 0 )) as NumberError,
count(*) as TotalRecords
from
log
where
site_id = ?
group by
DAYOFYEAR(created)

Related

mysql GROUP CONCAT not returning values

Here is my query
SELECT
SUM(o.order_disc + o.order_disc_vat) AS manualsale
FROM
orders o
WHERE
o.order_flag IN (0 , 2, 3)
AND o.order_status = '1'
AND (o.assign_sale_id IN (SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92))
AND DATE(o.payment_on) = DATE(NOW())
above query return null when i run this query in terminal
When i use subquery below it returns data
SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92)
above query returns
'106,124,142,179'
and when i run my first query like below
SELECT
SUM(o.order_disc + o.order_disc_vat) AS manualsale
FROM
orders o
WHERE
o.order_flag IN (0 , 2, 3)
AND o.order_status = '1'
AND (o.assign_sale_id IN (106,124,142,179))
AND DATE(o.payment_on) = DATE(NOW())
it return me value.
Why it is not working with subquery please help
This does not do what you want:
AND (o.assign_sale_id IN (SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92))
This compares a single value against a comma-separated list of values, so it never matches (unless there is just one row in users for the given team).
You could phrase this as:
AND assign_sale_id IN (SELECT id FROM users WHERE team_id = 92)
But this would probably be more efficently expressed with exists:
AND EXISTS(SELECT 1 FROM users u WHERE u.team_id = 92 AND u.id = o.assign_sale_id)
Side note: I would also recommend rewriting this condition:
AND DATE(o.payment_on) = DATE(NOW())
To the following, which can take advantage of an index:
AND o.payment_on >= current_date AND o.payment_on < current_date + interval 1 day

MySQL if statement returning incorrect results with aggregate function

I have a query
SELECT
itemId, userId, source
FROM table
WHERE itemId = 1 AND userId = 1 and source = 1;
It returns 20 rows where the itemId is 1, the userId is 1 and the source is 1 (for-instance). In my db there are more rows for this itemId and this userId, which have with different sources. I want to get some stats on this.
Why then does this query return 0 for both the unique and total rows?
SELECT ID,
IF(source = 1, COUNT(DISTINCT userId), 0) as 'unique',
IF(source = 1, COUNT(userId), 0) as 'total'
FROM table
WHERE itemId = 1 AND userId = 1;
GROUP BY itemId;
Thanks in advance.
use Conditional COUNT()
SELECT itemId
, COUNT( DISTINCT CASE WHEN source = 1
THEN userId
END) as 'unique'
, COUNT( CASE WHEN source = 1
THEN userId
END) as 'total'
FROM yourTable
WHERE itemId = 1 AND userId = 1
GROUP BY itemId

How to select last and last but one records

I have a table with 3 columns id, type, value like in image below.
What I'm trying to do is to make a query to get the data in this format:
type previous current
month-1 666 999
month-2 200 15
month-3 0 12
I made this query but it gets just the last value
select *
from statistics
where id in (select max(id) from statistics group by type)
order
by type
EDIT: Live example http://sqlfiddle.com/#!9/af81da/1
Thanks!
I would write this as:
select s.*,
(select s2.value
from statistics s2
where s2.type = s.type
order by id desc
limit 1, 1
) value_prev
from statistics s
where id in (select max(id) from statistics s group by type) order by type;
This should be relatively efficient with an index on statistics(type, id).
select
type,
ifnull(max(case when seq = 2 then value end),0 ) previous,
max( case when seq = 1 then value end ) current
from
(
select *, (select count(*)
from statistics s
where s.type = statistics.type
and s.id >= statistics.id) seq
from statistics ) t
where seq <= 2
group by type

Sum of two counts from different table with different conditions

Basically I need to merge these into one single query:
SELECT COUNT( DISTINCT id ) AS totalRows1
FROM other_events WHERE status = "approved"
AND Location = 1
SELECT COUNT( DISTINCT Id ) AS totalRows2
FROM core_events WHERE Status = "Active"
AND Location_id = 1
When I do it like below, if there is no event with Location_id = 1 query returns 0. In that condition I need it to return the count of the first table only.
SELECT COUNT( DISTINCT t1.id ) + COUNT( DISTINCT t2.Id ) AS total
FROM other_events AS t1, core_events AS t2
WHERE t1.status = "approved"
AND t1.Location = 1
AND t2.Location_id = 1
AND t2.Status = 'Active'
ps. column names are exactly like above
Use a UNION statement to merge the result like this:
SELECT SUM(total) FROM (
SELECT COUNT(DISTINCT id) AS total
FROM other_events
WHERE (status = "approved" AND Location = 1)
UNION ALL
SELECT COUNT(DISTINCT Id) AS total
FROM core_events
WHERE (Location_id = 1 AND Status = 'Active')
) union_result

View - Return 0 if no rows found in a grouped by query

Let's say I have the following MySQL view:
create or replace view total_transactions(account_id, total) as
select
t.account_id,
ifnull(sum(t.value), 0) as total
from transactions t
where t.paid IS TRUE
group by t.bank_account_id;
Let's say the account doesn't have any transaction yet, I want the view to return 0.
Right now, if I do a select like:
select * from total_transactions where account_id = 2060;
And account 2060 didn't had any transaction, it will return me nothing, instead of 0.
How could I fix that?
Thanks in advance.
EDIT
I think it could be something with the group by...
If I execute the query that I'm using for the view without the group by, it works (return 0 even with no results), but if I use group by it comes null:
select
t.account_id,
ifnull(sum(t.value), 0) as total
from transactions t
where t.paid IS TRUE
and account_id = 2060;
Returns 0, and
create or replace view total_transactions(account_id, total) as
select
t.account_id,
ifnull(sum(t.value), 0) as total
from transactions t
where t.paid IS TRUE
and account_id = 2060
group by t.bank_account_id;
Return an empty set.
If there is not an entry in the view results, then this will always return NULL - That's SQL. If you change your SELECT that you use against the view, you can achieve what you want:
SELECT IFNULL(total, 0) FROM total_transactions WHERE account_id = 2060
Edit:
(SELECT IFNULL(total, 0) total FROM total_transactions WHERE account_id = 2060)
UNION
(SELECT 0 total)
In production, don't use SELECT *. See this SO question for a thorough response as to why.
So, assuming you're not, you can use COALESCE, which will return the first non-null value.
SELECT
COALESCE(total, 0)
FROM
total_transactions
WHERE
account_id = '2600'
For positive values I use
select max(x.cnt) as cnt
from (
select ifnull(meta_value, 0) as cnt from wp_postmeta where post_id = 5543 and meta_key = '_bbp_voice_count'
union
select 0 as cnt) x
or for any
select x.cnt
from (
select ifnull(meta_value, 0) as cnt from wp_postmeta where post_id = 5543 and meta_key = '_bbp_voice_count'
union
select 0 as cnt
) x
limit 1