mySQL pivot dynamic dates table - mysql

i am running my query
SELECT
DATE_FORMAT(FROM_DAYS(TO_DAYS(st_day_request) -MOD(TO_DAYS(st_day_request) -2, 7)), '%d-%m-%Y') as DATE,
tx_report_id AS 'BUSINESS',
CONCAT(ROUND(AVG(DATEDIFF(dt_dossier_sent,st_day_request) > -5 AND (main.dt_dossier_sent IS NOT NULL AND main.dt_dossier_sent <> '0000-00-00')) * 100),'%') as DISPATCHED_5_AND_MORE,
CONCAT(ROUND(AVG(DATEDIFF(dt_dossier_sent,st_day_request) < -5 AND DATEDIFF(dt_dossier_sent,st_day_request) <= 1 AND (main.dt_dossier_sent IS NOT NULL AND main.dt_dossier_sent <> '0000-00-00')) * 100),'%') as DISPATCHED_4_TO_1,
CONCAT(ROUND(AVG(DATEDIFF(dt_dossier_sent,st_day_request) > 1 AND DATEDIFF(dt_dossier_sent,st_day_request) <= 10 AND (main.dt_dossier_sent IS NOT NULL AND main.dt_dossier_sent <> '0000-00-00')) * 100),'%') as DISPATCHED_2_TO_10,
CONCAT(ROUND(AVG(DATEDIFF(dt_dossier_sent,st_day_request) > 10 AND (main.dt_dossier_sent IS NOT NULL AND main.dt_dossier_sent <> '0000-00-00')) * 100),'%') as DISPATCHED_10_AND_MORE,
CONCAT(ROUND(AVG(main.dt_dossier_sent IS NULL OR main.dt_dossier_sent = '0000-00-00') * 100),'%') as OUTSTANDING FROM
(
SELECT
app_contract.id_app,
app_contract.dt_dossier_sent,
contract.tx_report_id,
contract.st_day_request
FROM
contract
INNER JOIN app_contract ON app_contract.id_contract = contract.id_contract AND contract.st_day_request > '2017-09-01' AND contract.id_client = 657
INNER JOIN app_hist ON app_hist.id_app=app_contract.id_app
GROUP BY app_contract.id_app
) as main GROUP BY DATE ORDER BY DATE_FORMAT(st_day_request, '%y') ASC, DATE_FORMAT(st_day_request, '%m') ASC
and getting the results on the image below:
Up to here, everything is correct. The Problem i have is that i want to change (pivot/crosstab) my results and put the dates i get as Columns, as you can see in the next image below:
I am trying for a couple of days but unfortunately without result. Any ideas?
PS. If you cannot see the images properly, please right click and open in new tab. Then you can enlarge them. Sorry for the images, i will copy-and paste the results in my next post.

Follow the tags -- there are many examples of how to do it in MySQL. Also, here is an automated way to generate SQL from a MySQL Stored Procedure: http://mysql.rjweb.org/doc.php/pivot
(Personally, I use generic PHP code that turns a 3-column table into a matrix.)

Related

how do I make this query run faster?

I have this SQL query running on a PHP website. This is an old site and the query was build by previous developer few years ago. But now as the site data is increased to around 230mb, this query has become pretty slow to execute. Take around 15-20 seconds. Is there any way I can make this run faster?
SELECT DISTINCT
NULL AS bannerID,
C1.url AS url,
LOWER(C1.Organization) AS company_name,
CONCAT(
'https://mywebsite.co.uk/logos/',
C1.userlogo
) AS logo_url
FROM
Company AS C1
INNER JOIN Vacancy AS V1 ON LOWER(V1.company_name) = LOWER(C1.Organization)
WHERE
V1.LiveDate <= CURDATE()
AND url = ''
AND V1.ClosingDate >= CURDATE()
AND C1.flag_show_logo = 1
As commented, your query is suffering from being non-sargable due to the use of lower function.
Additionally I suspect you can remove the distinct by using exists instead of joining your tables
select null as bannerID,
C1.url as url,
Lower(C1.Organization) as company_name,
Concat('https://mywebsite.co.uk/logos/', C1.userlogo) as logo_url
from Company c
where c.flag_show_logo = 1
and c.url = ''
and exists (
select * from Vacancy v
where v.LiveDate <= CURDATE()
and v.ClosingDate >= CURDATE()
and v.company_name = c.Organization
)
Avoid the sargable problem by changing to
ON V1.company_name = C1.Organization
and declaring those two columns to be the same collation, namely a collation ending with "_ci".
And have these composite indexes:
C1: INDEX(flag_show_logo, url, Organization, userlogo)
V1: INDEX(company_name, LiveDate, ClosingDate)
(These indexes should help Stu's answer, too.)

How to delete data with date value + time

I want to delete data between 'change_slot' and ('change_slot' + 2min) WHERE type = ' crash', 'pilot_death', or 'eject'
DELETE FROM pe_LogEvent
WHERE pe_LogEvent_type = 'crash'
OR pe_LogEvent_type = 'pilot_death'
OR pe_LogEvent_type = 'eject'
AND pe_LogEvent_datetime IN??? pe_LogEvent_type = 'change_slot'
BETWEEN ('change_slot datetime') AND (DATE_ADD('change_slot datetime', INTERVAL 120)))
Visual SQL DB Image with annotation of what I am trying to accomplish.:
I have another issue now, I'm trying to think of a way to solve this one as well. I was trying to use maybe a 'for each' iterative statement to filter only 1 pilotname at a time maybe? How would you go about solving this?
Problem: different pilot change_slot and causes current pilot's death not to count now ... I need to only have same pilot change_slot within 2 min pilot_death, crash, or eject does not count
DELETE FROM pe_LogEvent
WHERE pe_LogEvent_datetime BETWEEN
(SELECT pe_LogEvent_datetime FROM pe_LogEvent WHERE pe_LogEvent_type = 'change_slot' ORDER BY pe_LogEvent_datetime DESC LIMIT 1)
AND (SELECT pe_LogEvent_datetime + INTERVAL 2 MINUTES FROM pe_LogEvent WHERE pe_LogEvent_type = 'change_slot' ORDER BY pe_LogEvent_datetime DESC LIMIT 1)
AND pe_LogEvent_type IN ('crash', 'pilot_death', 'eject')
Somethink like this?
EDIT:
According to your comments below this answer, propably you want something like this, but i am not sure but if not, propably you could change this SQL at your own.
For such non-obvious queries i always use the CTE. I love it because you could use it to 'separate' your logic to simplest 'steps'. Then it's looks simple and easy to see what going on (At least for me) instead of one big query from which it is not so easy to deduce what is happening and is more difficult to edit/read.
First you could use the CTE to get all 'change_slot' type events with dates:
WITH change_slots_dates AS (
SELECT
pe_LogEvent_datetime AS date_start,
pe_LogEvent_datetime + INTERVAL 2 MINUTE AS end_time
FROM pe_LogEvent
WHERE pe_LogEvent_type = 'change_slot'
)
then another CTE to get IDs between this dates (Remember that your's CTE need to be separate by , sign:
rows_to_delete AS (
SELECT
pe.pe_LogEvent_id AS id
FROM pe_LogEvent AS pe
INNER JOIN change_slots_dates AS csd ON (pe.pe_LogEvent_datetime BETWEEN csd.date_start AND csd.end_time)
WHERE pe.pe_LogEvent_type IN ('crash', 'pilot_death', 'eject')
)
after that you can finally delete this entities:
DELETE FROM pe_LogEvent WHERE pe_LogEvent_id IN (SELECT id FROM rows_to_delete);
Notice that this is a one SQL query and you cannot use this as 3 queries. So all seems to be like this:
WITH change_slots_dates AS (
SELECT
pe_LogEvent_datetime AS date_start,
pe_LogEvent_datetime + INTERVAL 2 MINUTE AS end_time
FROM pe_LogEvent
WHERE pe_LogEvent_type = 'change_slot'
),
rows_to_delete AS (
SELECT
pe.pe_LogEvent_id AS id
FROM pe_LogEvent AS pe
INNER JOIN change_slots_dates AS csd ON (pe.pe_LogEvent_datetime BETWEEN csd.date_start AND csd.end_time)
WHERE pe.pe_LogEvent_type IN ('crash', 'pilot_death', 'eject')
)
DELETE FROM pe_LogEvent WHERE pe_LogEvent_id IN (SELECT id FROM rows_to_delete);
I hope it will be finally more helpfull.

MariaDB SELECT query with date_add()

I have this particular salect query which acts strange:
(1)
select date_add(
CURRENT_DATE(),
INTERVAL 7*0+(CASE
WHEN tutoring_disponibilities.day < weekday(CURRENT_DATE())
THEN 7+(tutoring_disponibilities.day-weekday(CURRENT_DATE()))
ELSE tutoring_disponibilities.day-weekday(CURRENT_DATE())
END
)
DAY
) from tutoring_disponibilities where date_add(
CURRENT_DATE(),
INTERVAL 7*0+(CASE
WHEN tutoring_disponibilities.day < weekday(CURRENT_DATE())
THEN 7+(tutoring_disponibilities.day-weekday(CURRENT_DATE()))
ELSE tutoring_disponibilities.day-weekday(CURRENT_DATE())
END
)
DAY
) NOT IN (SELECT date(tutoring_sessions.startDate) from tutoring_sessions);
This returns nothing, but this (which is the first part of the where):
(2)
select date_add(
CURRENT_DATE(),
INTERVAL 7*0+(CASE
WHEN tutoring_disponibilities.day < weekday(CURRENT_DATE())
THEN 7+(tutoring_disponibilities.day-weekday(CURRENT_DATE()))
ELSE tutoring_disponibilities.day-weekday(CURRENT_DATE())
END
)
DAY
)
from tutoring_disponibilities;
returns:
'2020-03-30'
'2020-03-30'
'2020-03-31'
'2020-03-31'
'2020-03-25'
'2020-03-25'
and this part:
(3)
SELECT date(tutoring_sessions.startDate) from tutoring_sessions;
returns this:
'2020-01-29'
NULL
NULL
NULL
'2020-02-05'
'2020-02-05'
'2020-02-10'
'2020-02-11'
'2020-02-18'
'2020-02-17'
'2020-02-25'
'2020-02-24'
'2020-03-02'
'2020-03-09'
'2020-03-16'
'2020-03-23'
'2020-02-13'
'2020-02-13'
'2020-02-13'
'2020-02-24'
'2020-02-29'
'2020-03-14'
'2020-03-30'
'2020-03-30'
'2020-03-30'
We can see that '2020-03-30'is in the results of query (3) and also in the results of query (2), but the other results of query (2) are not present in the results of query (3).
So why query(1) doesn't return anything?
If you know a better way to express this code I would be glad of any kind of help!
My recommendation is not to use NOT IN with subqueries. The reason is that any NULL value in the subquery causes no rows to be returned.
I recommend NOT EXISTS. However, your expression is rather complicated, so the simplest fix to your query is:
NOT IN (SELECT date(tutoring_sessions.startDate)
FROM tutoring_sessions
WHERE tutoring_sessions.startDate IS NOT NULL
)
With an index on tutoring_sessions(startDate), the equivalent NOT EXISTS may also be noticeably faster.

MySQL: Undesired result with max function on a timestamp

I use a Mantis Bug Database (that uses MySQL) and I want to query which bugs had a change in their severity within the last 2 weeks, however only the last severity change of the bug should be indicated.
The problem is, that I get multiple entries per bugID (which is the primary key), which is not my desired result since I want to have only the latest change per bug. This means that somehow I am using the max function and the group by clause wrongfully.
Here you can see my query:
SELECT `bug_id`,
max(date_format(from_unixtime(`mantis_bug_history_table`.`date_modified`),'%Y-%m-%d %h:%i:%s')) AS `Severity_changed`,
`mantis_bug_history_table`.`old_value`,
`mantis_bug_history_table`.`new_value`
from `prepared_bug_list`
join `mantis_bug_history_table` on `prepared_bug_list`.`bug_id` = `mantis_bug_history_table`.`bug_id`
where (`mantis_bug_history_table`.`field_name` like 'severity')
group by `bug_id`,`old_value`,`.`new_value`
having (`Severity_modified` >= (now() - interval 2 week))
order by bug_id` ASC
For the bug with the id 8 for example I get three entries with this query. The bug with the id 8 had indeed three severity changes within the last 2 weeks but I only want to get the latest severity change.
What could be the problem with my query?
max() is an aggregation function and it does not appear to be suitable for what you are trying to do.
I have feeling that what you are trying to do is to get the latest out of all the applicable bug_id in mantis_bug_history_table . If that is true, then I would rewrite the query as the following -- I would write a sub-query getLatest and join it with prepared_bug_list
Updated answer
Caution: I don't have access to the actual DB tables so this query may have bugs
select
`getLatest`.`last_bug_id`
, `mantis_bug_history_table`.`date_modified`
, `mantis_bug_history_table`.`old_value`
, `mantis_bug_history_table`.`new_value`
from
(
select
(
select
`bug_id`
from
`mantis_bug_history_table`
where
`date_modified` > unix_timestamp() - 14*24*3600 -- two weeks
and `field_name` like 'severity'
and `bug_id` = `prepared_bug_list`.`bug_id`
order by
`date_modified` desc
limit 1
) as `last_bug_id`
from
`prepared_bug_list`
) as `getLatest`
inner join `mantis_bug_history_table`
on `prepared_bug_list`.`bug_id` = `getLatest`.`last_bug_id`
order by `getLatest`.`bug_id` ASC
I finally have a solution! I friend of mine helped me and one part of the solution was to include the Primary key of the mantis bug history table, which is not the bug_id, but the column id, which is a consecutive number.
Another part of the solution was the subquery in the where clause:
select `prepared_bug_list`.`bug_id` AS `bug_id`,
`mantis_bug_history_table`.`old_value` AS `old_value`,
`mantis_bug_history_table`.`new_value` AS `new_value`,
`mantis_bug_history_table`.`type` AS `type`,
date_format(from_unixtime(`mantis_bug_history_table`.`date_modified`),'%Y-%m-%d %H:%i:%s') AS `date_modified`
FROM `prepared_bug_list`
JOIN mantis_import.mantis_bug_history_table
ON `prepared_bug_list`.`bug_id` = mantis_bug_history_table.bug_id
where (mantis_bug_history_table.id = -- id = that is the id of every history entry, not confuse with bug_id
(select `mantis_bug_history_table`.`id` from `mantis_bug_history_table`
where ((`mantis_bug_history_table`.`field_name` = 'severity')
and (`mantis_bug_history_table`.`bug_id` = `prepared_bug_list`.`bug_id`))
order by `mantis_bug_history_table`.`date_modified` desc limit 1)
and `date_modified` > unix_timestamp() - 14*24*3600 )
order by `prepared_bug_list`.`bug_id`,`mantis_bug_history_table`.`date_modified` desc

My join sql query won't bring results

What could be wrong with my sql query here , I'd like to retrieve data from both tables meeting a WHERE condition
SELECT *, UNIX_TIMESTAMP(i.sent_date) AS udate
FROM ibc_sent_history as i INNER JOIN
ibc_messages as u
ON i.msg_ids = u.id
WHERE (i.sent_date >= '02-02-2013' AND i.sent_date <= '02-02-2014')
ORDER BY i.sent_date
LIMIT 200
Assuming your ibc_sent_history.sent_date datatype is DATETIME, here's a way to refactor this query. (This will work even if the datatype is DATE). You need to change your date input string format from 02-02-2013 to the more standard '2014-02-02` (YYYY-MM-DD).
SELECT whatever, whatever
FROM ibc_sent_history AS i
INNER JOIN ibc_messages AS u ON i.msg_ids = u.id
WHERE i.sent_date >= '2013-02-02'
AND i.sent_date < '2014-02-02' + INTERVAL 1 DAY
ORDER BY i.sent_date DESC
LIMIT 200
I changed the ORDER BY to include DESC. This is to return the most recent items, not the oldest. If that's not what you need, take off the DESC.
I changed the date formatting.
I changed the end of your selection range to
i.sent_date < '2014-02-02` + INTERVAL 1 DAY
That's because
i.sent_date <= '2014-02-02`
will include items that occur precisely at midnight on 2-Feb-2014, but won't include any other items on that day. What you probably want are items that occurred up to but NOT including midnight on the next day.
I don't know MySQL very well, but in SQL Fiddle when I run:
CAST('2014-02-02' AS DATE)
I get a date, when I run
CAST('02-02-2014' AS DATE)
I get NULL, so seems like your date format is wrong.
Demo: SQL Fiddle