I am working ona small project for a client, and the data in the database is causing me a bit of bother. I am completing a search form for paid installments from the database. I can get the data fine, its the date fields which are giving me bother.
There are separate fields for the date, month and year for each installments. Is there a way I can use CONCAT or DATE or maybe even use CAST on the combined fields to search for the data.
I have tried the following
SELECT DATE_FORMAT( CONCAT( received_date, received_month, received_year ) , '%d/%m/%Y' ) FROM policy_installments
AND
SELECT DATE_FORMAT(STR_TO_DATE(CONCAT('pit.received_date', '-', 'pit.received_month','-', 'pit.received_year'), '%d-%m-%Y') AS mydate, '%d-%m-%Y') AS modate FROM policy_installments pit ORDER BY mydate
AND
SELECT * FROM policy_installments WHERE CONCAT(CAST(received_date AS CHAR),"-",CAST(received_month AS CHAR),"-",CAST(received_year AS CHAR)) >= '1-1-2014' AND CONCAT(CAST(received_date AS CHAR),"-",CAST(received_month AS CHAR),"-",CAST(received_year AS CHAR)) <= '31-3-2014'
When I use the following I get some unexpected data:
SELECT CONCAT_WS( received_date, "-", received_month, "-", received_year ) FROM policy_installments
RESULT
2d313130312d3132303039
2d3237313032372d323732303039 etc etc
Each of the 3 fields are type INT(11).
However, none of these give me the correct data. Is there a way i can convert all 3 fields and use a date to pass into the query to enable me to correctly grab the correct data?
Any help would be much appreciated!
To convert a concatenated string to date, string should be in the same format as what you are using to convert.
For a date-string in dd/mm/yyyy format, should be converted to sql date using %d/%m%Y format.
Example:
SELECT STR_TO_DATE(
CONCAT( received_date, '/', received_month, '/', received_year ),
'%d/%m/%Y'
) as rcvd_date
FROM policy_installments
And to compare with other dates, you can use BETWEEN clause.
Example:
SELECT * FROM policy_installments
WHERE
STR_TO_DATE(
CONCAT( received_date, '-', received_month, '-', received_year ),
'%d-%m-%Y'
) BETWEEN STR_TO_DATE( '1-1-2014', '%d-%m-%Y' )
AND STR_TO_DATE( '31-3-2014', '%d-%m-%Y' )
Refer to:
MySQL: STR_TO_DATE(str,format): Convert a string to a date
Generally, you want to avoid performing the function on the column. Instead, perform the function on the literal, so that you can utilize indexes on the columns:
SELECT *
FROM policy_installments
WHERE received_year = YEAR('2014-03-31')
AND received_month = MONTH('2014-03-31')
AND received_date = DAY('2014-03-31')
Add a multi-column index on received_year, received_month, and received_date, you'll be doing pretty good.
Obviously, this method will only work for = comparisons. For <=, it becomes a bit more complicated:
SELECT *
FROM policy_installments
WHERE
received_year < YEAR('2014-03-31')
UNION
SELECT *
FROM policy_installments
WHERE
received_year = YEAR('2014-03-31')
AND received_month < MONTH('2014-03-31')
UNION
SELECT *
FROM policy_installments
WHERE
received_year = YEAR('2014-03-31')
AND received_month = MONTH('2014-03-31')
AND received_date <= DAY('2014-03-31')
But this longer query is still able to utilize the index.
To simplify things, consider creating an actual date column and keep it updated with the proper value.
Related
I'm working on a legacy database system which deals with property reports and I am attempting to update a query which shows multiple reports within a time period for the same address. I am using MySQL Workbench to run this query.
The table has a UPRN field for unique addresses, which is fine, however addresses can be manually added without a UPRN. The table also has address1, address2, address3, town, postcode fields. There is NO field which contains the whole address as one line.
We already have a query which uses the UPRN field to generate a list of multiple records within a timescale; however, this will obviously exclude multiple reports without a UPRN.
SELECT * FROM
`rep_base_report` `rep`
WHERE
`rep`.`UPRN` IN (SELECT
`rep2`.`UPRN` AS `UPRN`
FROM
`rep_base_report` `rep2`
WHERE
`rep2`.`STATUS` = 'LODGED'
AND `rep2`.`GENERATED_DATE` > (CURDATE() - INTERVAL 3 MONTH)
GROUP BY `rep2`.`UPRN`
HAVING (COUNT(0) > 1)
ORDER BY `rep2`.`GENERATED_DATE` DESC)
GROUP BY `rep`.`RepReference`
ORDER BY `rep`.`UPRN`
In my head, if I could CONCAT address1, address2 and postcode and use this as the basis for the check, it would give the appropriate output, but I've no idea how to accomplish this.
I've attempted the following but it's giving fewer values that I was expect (I've manually checked some sample data in Excel to see what I should be expecting).
SELECT *
FROM `rep_base_report` `rep`
WHERE
#property :=CONCAT(`rep`.`ADDRESS1`, ' ' ,`rep`.`ADDRESS2`, ' ' ,`rep`.`POSTCODE`) IN (
SELECT #property2 :=CONCAT(`rep2`.`ADDRESS1`, ' ' ,`rep2`.`ADDRESS2`, ' ' ,`rep2`.`POSTCODE`) AS `PROPERTY`
FROM `rep_base_report` `rep2`
WHERE `rep2`.`STATUS` = 'LODGED'
AND `rep2`.`GENERATED_DATE` > CURDATE() - INTERVAL 3 MONTH
GROUP BY #property2
HAVING COUNT(0) > 1
ORDER BY `rep2`.`GENERATED_DATE` DESC
)
GROUP BY `rep`.`RepReference`
ORDER BY `rep`.`UPRN`
SELECT *
FROM `rep_base_report` `rep`
WHERE
CONCAT(`rep`.`ADDRESS1`, ' ', `rep`.`ADDRESS2`, ' ', `rep`.`POSTCODE`) IN (
SELECT CONCAT(`rep2`.`ADDRESS1`, ' ', `rep2`.`ADDRESS2`, ' ', `rep2`.`POSTCODE`) AS `PROPERTY`
FROM `rep_base_report` `rep2`
WHERE `rep2`.`STATUS` = 'LODGED'
AND `rep2`.`GENERATED_DATE` > CURDATE() - INTERVAL 3 MONTH
GROUP BY CONCAT(`rep2`.`ADDRESS1`, ' ', `rep2`.`ADDRESS2`, ' ', `rep2`.`POSTCODE`)
HAVING COUNT(0) > 1
ORDER BY `rep2`.`GENERATED_DATE` DESC
)
GROUP BY `rep`.`RepReference`
ORDER BY `rep`.`UPRN`
I have created a query that gives me a sum of sales for each department for the day.
What I want is to generate this data for the whole month using one master query.
This is the result I expect to see:
Department No Department Name 1-April 2-April 3-April
1 Infants 100 112 96
2 Kids 120 132 123
Total Total 220 144 219
Currently, I can generate this for one day only, and I have to keep changing the date value to get the data for the next day.
As stated earlier, I want to run a this for the whole month in one master query.
Please try the following...
SELECT CONCAT( 'SELECT department.id AS `Department No`,
department.departmentName AS `Department Name`',
GROUP_CONCAT( ', SUM( date_',
DATE_FORMAT( salesDate, '%d_%M' ),
'.sales ) AS `',
DATE_FORMAT( salesDate, '%e-%M' ),
'`',
SEPARATOR '' ),
' FROM department',
GROUP_CONCAT( ' LEFT JOIN departmentSales AS date_',
DATE_FORMAT( salesDate, '%d_%M' ),
' ON department.id = date_',
DATE_FORMAT( salesDate, '%d_%M' ),
'.department_id AND date_',
DATE_FORMAT( salesDate, '%d_%M' ),
'.salesDate = `',
DATE_FORMAT( salesDate, '%Y-%M-%d' ),
'`',
SEPARATOR '' ),
' GROUP BY department.id
ORDER BY department.id
UNION
SELECT `Totals`,
` `,
GROUP_CONCAT( ', SUM( date_',
DATE_FORMAT( salesDate, '%d_%M' ),
'.sales )',
SEPARATOR '' ),
' FROM department',
GROUP_CONCAT( ' LEFT JOIN departmentSales AS date_',
DATE_FORMAT( salesDate, '%d_%M' ),
' ON department.id = date_',
DATE_FORMAT( salesDate, '%d_%M' ),
'.department_id AND date_',
DATE_FORMAT( salesDate, '%d_%M' ),
'.salesDate = `',
DATE_FORMAT( salesDate, '%Y-%M-%d' ),
'`',
SEPARATOR '' ),
' ) INTO #salesQuery
FROM ( SELECT DISTINCT salesDate
FROM departmentSales
WHERE salesDate BETWEEN FROM_DAYS( TO_DAYS( targetDate ) -
DAYOFMONTH( targetDate ) +
1
)
AND LAST_DAY( targetDate )
) AS salesDateFinder;
PREPARE salesTotals FROM #salesQuery;
EXECUTE salesTotals;
This attempt is based on code found at mysql select dynamic row values as column names, another column as value. I am concerned about the group_concat_max_len variable being a problem. If it is then I'll step up my research into the CASE method eggyal mentioned. If it is not a problem, then I'll take things a bit easier.
Most of the lines here actually form just one SQL statement. It takes the list of salesDate values constructed by the subquery near the end, which are then used to help construct a rather long string containing the SQL statement to be run against the database.
The string is formed from a list of substrings specified as the arguments to the function CONCAT(), which you can read about at https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_concat.
The string starts by specifying that the fields id and departmentName from the table department should be the first selected for each record, followed by the SUM() of each date's sales for that department. Each date field is given a name like date_09_March and in the output a heading like 9-March.
The repeated segments of the string are achieved by using the GROUP_CONCAT function, which you can read about at https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_group-concat.
After the fields are specified the string specifies that the values should come from a temporary table formed from department LEFT JOINed with an instance of the departmentSales table for each date, with each instance being given an alias like date_09_March and joined on its department_id field to the id field of department.
Once it is formed, the temporary table's records are grouped by department.id so that the totals for each department may be calculated. The output is then sorted by department.id.
The statement used to produce this sorted output and its column headings is then joined to another (somewhat simpler) statement used to generate the totals row. The string containing the SQL statement formed by the concatenation of each substring is then assigned to the variable #salesQuery, prepared and executed.
If you have any questions or comments, then please feel free to post a Comment accordingly.
Further Reading
https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html (for information on the DATE functions used)
Thanks for all the answers
I managed to write a query based on the Pivot Function.
You can control the range of dates with something like this:
select (...) where v_date >= 1-April and v_date <= 30-April
(consider v_date the name of the column and 1-April in date format)
The below mentioned query is returning null but my inner query is fetching values like
SELECT CONCAT(
FLOOR(HOUR(time_milis) / 24), ' ',
MOD(HOUR(time_milis), 24), ':',
MINUTE(time_milis),':',
SECOND(time_milis))
FROM (
SELECT SUM(TIMEDIFF(logout_timestamp,login_timestamp)) AS time_milis FROM t_access_log WHERE logout_timestamp!='0000-00-00 00:00:00' GROUP BY login_id
)se
what am i missing here
Why not using DATE_FORMAT() for this? So my guess would be something like:
SELECT login_id ,
DATE_FORMAT(
SUM(
TIMEDIFF(
logout_timestamp,login_timestamp)
)
) as some_time
FROM t_access_log
WHERE logout_timestamp!='0000-00-00 00:00:00'
GROUP BY login_id;
Your time_milis are all seconds, but hour need a time like '10:05:03', you can just use sec_to_time. Like this:
SELECT concat(hour(sec_to_time(time_milis)), ' ', sec_to_time(time_milis))
FROM (
SELECT SUM(TIMEDIFF(logout_timestamp,login_timestamp)) AS time_milis FROM t_access_log WHERE logout_timestamp!='0000-00-00 00:00:00' GROUP BY login_id
)se
I'm trying to get a date value with:
- Current year
- Current month
- Day number from one select
the query
SELECT rat_data FROM rate_unita WHERE rateid = 1
as a result one INT value.
When I try to execute the following:
SELECT DATE(CONCAT(YEAR(NOW())),(MONTH(NOW())), (SELECT rat_data FROM rate_unita WHERE rateid = 1))
Mysql mark my syntax as incorrect near ' ( MONTH( NOW( ) ) ) , ( SELECT rat_data FROM rate_unita WHERE r
I think that there is something wrong with the way I'm calling the SELECT in the CONCAT, what is the correct query to reach my goal?
Thanks
L
You're closing the concat function too early. Remove some extra parentheis
SELECT DATE(CONCAT(YEAR(NOW()),
MONTH(NOW()),
(SELECT rat_data FROM rate_unita WHERE rateid = 1)
))
But you need to add some hyphens to make it a true date value
SELECT DATE(
CONCAT(
YEAR(NOW()),
'-',
MONTH(NOW()),
'-',
(SELECT rat_data FROM rate_unita WHERE rateid = 1)
)
)
This should result in a date of YEAR-MONTH-rat_data
Here's a working SQL Fiddle
Use DATE_FORMAT function. Here are a few examples:
http://www.w3schools.com/sql/func_date_format.asp
http://www.mysqltutorial.org/mysql-date_format/
Help me with next SQL:
SELECT
date_format(from_unixtime(`ticket_logs`.`created`),'%Y-%m-%d') AS `datac`,
`ticket_logs`.`ticket_id` AS `ticket_id`,
ticket_logs.value_old,
ticket_logs.value_new,
max(`ticket_logs`.`action`) AS `ultima_act`
FROM
`ticket_logs`
WHERE
(
(`ticket_logs`.`action` = 6)
OR (`ticket_logs`.`action` = 16)
)
GROUP BY
date_format(
from_unixtime(`ticket_logs`.`created`),
'%Y-%m-%d'
),
`ticket_logs`.`ticket_id`
ORDER BY
`datac` DESC,
`ticket_logs`.`ticket_id` DESC
The problem is that "value_old" and "value_new", always take the first value per date and not the value corresponding with the max value of "action"
I don't see how this is a problem. That is how SQL works -- the order by takes place after the group by. In addition, MySQL is just confusing you, because you are using an extension to group by that you don't fully understand -- having extra columns in the select that are not in the group by. (See this.)
Fortunately, MySQL supports a hack to get what you want, without writing a much more complicated SQL statement. The expressions you want are:
substring_index(group_concat(ticket_logs.value_old order by `ticket_logs`.`action` desc), ',', 1)
substring_index(group_concat(ticket_logs.value_new order by `ticket_logs`.`action` desc), ',', 1)
Found another approach. I used the "created" column to obtain a max and joined:
SELECT
date_format(
from_unixtime(`ticket_logs`.`created`),
'%Y-%m-%d'
) AS `datax`,
ticket_logs.ticket_id,
ticket_logs.action,
ticket_logs.value_old,
ticket_logs.value_new
FROM
ticket_logs
INNER JOIN (
SELECT
date_format(
from_unixtime(`ticket_logs`.`created`),
'%Y-%m-%d'
) AS `datac`,
max(ticket_logs.created) AS maxts,
ticket_id
FROM
ticket_logs
WHERE
ticket_logs.action = 6
OR ticket_logs.action = 16
GROUP BY
date_format(
from_unixtime(`ticket_logs`.`created`),
'%Y-%m-%d'
),
ticket_id
) maxtbl ON ticket_logs.ticket_id = maxtbl.ticket_id
AND ticket_logs.created = maxtbl.maxts
ORDER BY
datax DESC,
ticket_id DESC