using an aggregate value along with non-agrregate values in SQL - mysql

I need to use the average value of the column 'sales' from a table called 'previous_target' and compare that value with the individual rows of the same column 'sales' of the same table .
I get the required result when I disable the SQL mode from only_full_group_by.
But i would like to know if there is a better way to write the code without disabling the full group by mode.
Here is an example SQL query.
select
f.id,t.sale as previous_target,
case
when t.sale > 1.25*(avg(t.sale)) then round((avg(t.sale)*1.1),2)
when t.sale < 0.9*(avg(t.sale)) then round((avg(t.sale)*0.9),2)
else
t.sale
end
as current_target from
details f
inner join prev_target t on f.l_number=t.l_number
inner join time_details ft on ft.id=f.id
note:if i add the line
group by f.id,f.l_number,t.sale
it just copies the same value onto the current_target column .
can anyone suggest a way to use the average of the sales column from the prev_target table and compare it with each row of the same table with the given conditions.
I hope I conveyed my requirement without causing much confusion.

SELECT f.id, t.sale AS previous_target,
CASE
WHEN t.sale > 1.25*(a.sale) then round(((a.sale)*1.1),2)
WHEN t.sale < 0.9*(a.sale) then round(((a.sale)*0.9),2)
ELSE
t.sale
END AS current_target
FROM details f
INNER JOIN prev_target t ON f.l_number = t.l_number
INNER JOIN
(
SELECT avg(t.sale) AS sale, t.l_number FROM prev_target t GROUP BY t.l_number
)AS a ON t.l_number = a.l_number
INNER JOIN time_details ft ON ft.id = f.id

Using OVER clause would be best recommended here for aggregation. I have created sample run for you (ofcourse different data and columns) but you will get the gist.
create table sales(amount int);
insert into sales values(5);
insert into sales values(10);
insert into sales values(15);
insert into sales values(20);
insert into sales values(10);
select * from sales;
select avg(amount) average from sales;
select amount, case when amount >= (avg(amount) over (PARTITION BY 1)) then 'Good' else 'Bad' end as type from sales;
Result >>

Related

MySQL - Split SELECT query (WHERE IN) into two rows

So, I am using MySQL to do a query and have a database like this:
I wanted to do a select query to show every transaction of Bank A and C based on their prefix. This is the expected result:
I have done the query as followed:
SELECT
M.merk AS 'Merk',
COUNT( T.amount ) AS 'Jumlah Transaksi',
SUM( T.amount ) AS 'Total Amount'
FROM
tb_transaksiatm T
INNER JOIN tb_issuer I ON
T.nomor_kartu LIKE CONCAT(I.prefix, '%')
INNER JOIN tb_terminalatm M ON
T.terminal = M.nomor_terminal
WHERE
I.bank IN ('A', 'C') # Declare the Bank;
But my result is not the same as expected. It combined and summed both rows from Bank A and Bank C. This is my result:
The question is, how do I split the WHERE IN condition into two rows? Any help would be appreciated. Thank you.
Note: The language is Indonesian.
SELECT
M.merk AS 'Merk',
COUNT( T.amount ) AS 'Jumlah Transaksi',
SUM( T.amount ) AS 'Total Amount'
FROM
tb_transaksiatm T
INNER JOIN tb_issuer I ON
T.nomor_kartu LIKE CONCAT(I.prefix, '%')
INNER JOIN tb_terminalatm M ON
T.terminal = M.nomor_terminal
WHERE
I.bank IN ('A', 'C') # Declare the Bank
group by M.merk;
When you use an aggregation function such as SUM or COUNT and you do not specify a GROUP BY, it will aggregate all rows together. Fields such as M.Merk that could vary between the rows being aggregated will have a value taken from an arbitrary one of the rows being aggregated, though modern versions of mysql default to an ONLY_FULL_GROUP_BY mode where selecting such a field will result in an error instead of an arbitrary value.
It sounds like you intend to have a GROUP BY I.Bank, M.Merk, though it is confusing that you don't include the bank in your selected fields.

Conditionally counting while also grouping by

I am trying to join two tables
ad_data_grouped
adID, adDate (date), totalViews
This is data that has already been grouped by both adID and adDate.
The second table is
leads
leadID, DateOfBirth, adID, state, createdAt(dateTime)
What I'm struggling with is joining these two tables so I can have a column that counts the number of leads when it shares the same adID and where the adDate = createdAt
The problem I'm running into is that when the counts are all the same for all groupings of adID....I have a few other things I'm trying to do, but it's based on similar similar conditional counting.
Query:(I know the temp table is probably overkill, but I'm trying to break this up into small pieces where I can understand what each piece does)
CREATE TEMPORARY TABLE ad_stats_grouped
SELECT * FROM `ad_stats`
LIMIT 0;
INSERT INTO ad_stats_grouped(AdID, adDate, DailyViews)
SELECT
AdID,
adDate,
sum(DailyViews)
FROM `ad_stats`
GROUP BY adID, adDate;
SELECT
ad_stats_grouped.adID,
ad_stats_grouped.adDate,
COUNT(case when ad_stats_grouped.adDate = Date(Leads.CreatedAt) THEN 1 ELSE 0 END)
FROM `ad_stats_grouped` INNER JOIN `LEADS` ON
ad_stats_grouped.adID = Leads.AdID
GROUP BY adID, adDate;
The problem with your original query is the logic in the COUNT(). This aggregate functions takes in account all non-null values, so it counts 0 and 1s. One solution would be to change COUNT() to SUM().
But I think that the query can be furtermore improved by moving the date condition on the date to the on part of a left join:
select
g.adid,
g.addate,
count(l.adid)
from `ad_stats_grouped` g
left join `leads` l
on g.adid = l.adid
and l.createdat >= g.addate
and l.createdat < g.ad_stats + interval 1 day
group by g.adid, g.addate;

MySQL error 1351 Can't Create a View

Below is a query that I'm trying to create a view with. When I run it, "I get Error Code: 1351. View's SELECT contains a variable or parameter". WHy is this the case and is there a way around this that I can create a view without changing too much of what I have in my current query?
Thanks!!
create view delta as
select rnk2.`date`,
case when rnk1.r1=1 and rnk2.r2=1 then rnk1.X else rnk2.X-rnk1.X end as 'Daily Total'
from (
select `date`,X,#r1:=#r1+1 as r1
from samples, (select #r1:=0) a
order by `date` ) rnk1
inner join
(select `date`,X,#r2:=#r2+1 as r2
from samples, (select #r2:=0) b
order by `date`) rnk2
on (rnk1.r1=1 and rnk2.r2=1) or (rnk1.r1+1=rnk2.r2)
order by rnk2.`date`;
mySQL views does not allow user variables nor subqueries, so I changed the query and split the view into two parts.
The first view will assign a row number on the table SAMPLE according to date. Then the 2nd view will make use of the first view (sample_vw) to do the main query.
create view sample_vw as
select a.`date`, a.X, count(*) as rn
FROM samples a
JOIN samples b
ON a.`date` >= b.`date`
GROUP BY a.`date`;
create view delta as
SELECT t1.`date`,
case when t1.rn=1 and t2.rn=1 then t1.X else t2.X-t1.X end as 'Daily Total'
FROM sample_vw t1
INNER JOIN sample_vw t2
ON t1.rn+1=t2.rn or (t1.rn=1 and t2.rn=1);

Display zero in group by sql for a particular period

I am trying to run the following query to obtain the sales for each type of job for a particular period. However for certain months where there are no jobs of a particular job type performed no 0 is displayed in sales.
How can i display the zeros in such a condition.
Here is the sql query-
select Year(postedOn), month(postedOn), jobType, sum(price)
from tbl_jobs
group by jobType, year(postedOn), month(postedOn)
order by jobType, year(postedOn), month(postedOn)
Typically, this is where your all-purpose calendar or numbers table comes in to anchor the query with a consistent sequential set:
SELECT job_summary.*
FROM Calendar
CROSS JOIN (
-- you may not have though about this part of the problem, though
-- what about years/months with missing job types?
SELECT distinct jobType FROM tbl_jobs
) AS job_types
LEFT JOIN (
select Year(postedOn) AS year,month(postedOn) as month,jobType ,sum(price)
from tbl_jobs
group by jobType, year(postedOn), month(postedOn)
) job_summary
ON job_summary.jobType = job_types.jobType
AND job_summary.year = Calendar.year
AND job_summary.month = Calendar.month
WHERE Calendar.day = 1 -- Assuming your calendar is every day
AND calendar.date BETWEEN some_range_goes_here -- you don't want all time, right?
order by job_types.jobType, Calendar.year, Calendar.month

Populate one table with selected data from two others tables

I have a table that I am trying to populate with our open tickets and the last followup they received, columns below, this is called "glpi_plugin_ns_followup":
ticket_number[int(11)], datetime_created[datetime], requester[int(11)], title[longtext], description[longtext], followup_datetime[datetime], last_followup[longtext], technician_user[int(11)]
The tables I am pulling from are glpi_tickets and glpi_ticketfollowups
glpi_tickets
id[int(11)], entities_id[int(11)], name[varchar(255)], date[datetime], closedate[datetime], solvedate[datetime], date_mod[datetime], users_id_lastupdater[int(11)], status[int(11)], users_id_recipient[int(11)], requesttypes_id[int(11)], content, urgency[int(11)], impact[int(11)], priority[int(11)], itilcategories_id[int(11)], type[int(11)], solutiontypes_id[int(11)], solution, global_validation[int(11)], slas_id[int(11)], slalevels_id[int(11)], due_date[datetime], begin_waiting_date[datetime], sla_waiting_duration[int(11)], waiting_duration[int(11)], close_delay_stat[int(11)], solve_delay_stat[int(11)], takeintoaccount_delay_stat[int(11)], actiontime[int(11)], is_deleted[tinyint(1)], locations_id[int(11)], validation_percent[int(11)]
glpi_ticketfollowups
id[int(11)], tickets_id[int(11)], date[datetime], users_id[int(11)], content[longtext], is_private[tinyint(11)], requesttypes_id[int(11)]
The common identifier across all three are as follows:
glpi_tickets = id
glpi_ticketfollowups = tickets_id
glpi_plugin_ns_followup = ticket_number
I've managed to insert the first five columns by using the below query:
TRUNCATE glpi_plugin_ns_followup;
INSERT INTO glpi_plugin_ns_followup (ticket_number, datetime_created, requester, title, description)
SELECT Id, date, users_id_recipient, name, content FROM glpi.glpi_tickets
WHERE is_deleted = 0 and status !=6 and status !=5;
However I am struggling to fill in the last 3 columns with the relevant updates, so far I have tried using this code but it doesn't work:
UPDATE glpi_plugin_ns_followup
SET followup_datetime = (SELECT date from glpi_ticketfollowups where glpi_ticketfollowups.tickets_id = glpi_plugin_ns_followup.ticket_number),
last_followup = (SELECT content from glpi_ticketfollowups where glpi_ticketfollowups.tickets_id = glpi_plugin_ns_followup.ticket_number),
technician_user = (SELECT users_id from glpi_ticketfollowups where glpi_ticketfollowups.tickets_id = glpi_plugin_ns_followup.ticket_number)
This gives me the response:
Error Code: 1242. Subquery returns more than 1 row
Which it should as I'm trying to update multiple rows, or the error may be because there are multiple followups per ticket. I have a feeling I may be going at this from entirely the wrong angle. If someone can assist me and put me back on the right track that would be much appreciated.
I can supply sample data if needed but it will take a while to gather and censor it ;-)
EDIT: As per Kamil's suggestions with max() I have managed to get the date fields to work:
UPDATE glpi_plugin_ns_followup
SET followup_datetime = (SELECT max(date) from glpi_ticketfollowups where glpi_ticketfollowups.tickets_id = glpi_plugin_ns_followup.ticket_number GROUP BY tickets_id)
However I cannot get it to work for content or technician_user, if I can pull the newest date/times for the last time a followup was added to a ticket can I modify anything to pull in the content and technician_user as well?
EDIT2: tried adding GROUP BY t.id; at the end of your initial solution but it does not use the newest followup for the tickets.
EDIT3:Have tried the following, it pulls in the correct date/time but the wrong content
TRUNCATE glpi_plugin_ns_followup;
INSERT INTO glpi_plugin_ns_followup (ticket_number, datetime_created, requester, title, description, followup_datetime, last_followup, technician_user)
SELECT
t.id, t.date, t.users_id_recipient, t.name, t.content,
tf.datetime, tf.content, tf.users_id
FROM
glpi.glpi_tickets t
LEFT JOIN (SELECT max(date) as datetime, content, users_id, tickets_id
FROM glpi_ticketfollowups
GROUP BY tickets_id) tf ON
t.id = tf.tickets_id
WHERE
t.is_deleted = 0
and t.status NOT IN (5,6)
GROUP BY t.id;
Thanks
iFr4g
You could combine both of those statements into one and only issue INSERT command. However, this will multiply your rows from glpi_tickets if more than one matching row exists in glpi_ticketfollowups fetched with the condition t.id = tf.tickets_id.
I've added a LEFT JOIN and slightly changed the WHERE clause to save some coding.
INSERT INTO glpi_plugin_ns_followup (ticket_number, datetime_created, requester, title, description, followup_datetime, last_followup, technician_user)
SELECT
t.id, t.date, t.users_id_recipient, t.name, t.content,
tf.date, tf.content, tf.users_id
FROM
glpi.glpi_tickets t
LEFT JOIN glpi_ticketfollowups tf ON
t.id = tf.tickets_id
WHERE
t.is_deleted = 0
and t.status NOT IN (5,6)
If you need only one row per glpi_tickets table in your destination table, then what you are looking for is a way to get only one row from glpi_ticketfollowups per a ticket. This could be achieved by adding a GROUP BY clause and choosing appropriate logic of getting one value, for example: max() or min(). Remember, that issuing an aggregate function over 3 values will not bring you result from one row, but an aggregate result from entire dataset. This could be avoided by doing it the way you started - insert and update by adding a LIMIT clause.
Edit after comments:
Since you need last followup for a every ticket and you are sure that are dates per ticket are unique you could do a self-join calculating maximum date per ticket in glpi_ticketfollowups and then LEFT JOIN that to your glpi_tickets table:
INSERT INTO glpi_plugin_ns_followup (ticket_number, datetime_created, requester, title, description, followup_datetime, last_followup, technician_user)
SELECT
t.id, t.date, t.users_id_recipient, t.name, t.content,
tf.date, tf.content, tf.users_id
FROM
glpi.glpi_tickets t
LEFT JOIN (
SELECT allf.tickets_id, allf.date, allf.content, allf.users_id
FROM glpi_ticketfollowups allf
INNER JOIN (
SELECT tickets_id, MAX(date) AS date
FROM glpi_ticketfollowups
GROUP BY tickets_id
) lastf ON
allf.tickets_id = lastf.tickets_id
AND allf.date = lastf.date
) tf ON
t.id = tf.tickets_id
WHERE
t.is_deleted = 0
and t.status NOT IN (5,6)