get age using year of birth only on perl - mysql

i have a mysql table called subscribers like this:
cell |name |year|id|email
10000000|Arabang,GP|F|1981|2|10000000#cs.ub.bw
10000001|Basupang,B.|F|1981|42|10000001#cs.ub.bw
10000002|Bontshitswe,C.|F|1980|36|10000002#cs.ub.bw
10000003|Botsalano,T.|M|1980|42|10000003#cs.ub.bw
10000004|Botshelo,M.|F|1982|14|10000004#cs.ub.bw
10000005|Botshelo,OD|M|1981|10|10000005#cs.ub.bw
10000006|Chabota,M|M|1980|3|10000006#cs.ub.bw
how do i calculate age using the year only .i have researched on this website
but still i cant understand how to do that.
*the data type of year is year *
here is my query :
my $sth0 =$dbh-> prepare("select *,( 2014 - year)
as year from subscribers
");
$sth0->execute() or die $DBI::errstr;

I don't understand what your question is. Why do you think that this doesn't work? If you ignore the fact that the age you get this way might be a year too high (depending on when the person's birthday is) then your query works.
[test]> create table person (name varchar(20), birth year);
Query OK, 0 rows affected (0.04 sec)
[test]> insert into person values ('Dave', 1962);
Query OK, 1 row affected (0.02 sec)
[test]> select name, year(now()) - birth as age from person;
+------+------+
| name | age |
+------+------+
| Dave | 52 |
+------+------+
1 row in set (0.00 sec)
(And that's a year too high, as my birthday is in September and it's only April now.)
Note: I changed your hard-coded '2014' to year(now()) so the query continues to work the same way in the future.

Related

LEFT vs. DATE_FORMAT in mysql

LEFT or DATE_FORMAT, which is faster to re-format date in SELECT query in mysql?
I'll show an example of this problem.
Info of PERSON table
Column name
Type
NAME
VARCHAR(20)
YEAR
DATETIME
PERSON
NAME
YEAR
Travis
2020-01-01
Sam
2021-01-01
If execute 'SELECT YEAR FROM PERSON' query, can see below result.
YEAR
2020-01-01 00:00:00
2021-01-01 00:00:00
But I want result like below.
YEAR
2020-01-01
2021-01-01
So, I wanted use one of below queries.
SELECT LEFT(YEAR,10) FROM PERSON
SELECT DATE_FORMAT(YEAR, '%Y-%m-%d')
However, I wonder what query perform better.
Please, help me..
Technically, LEFT() is nearly four times faster, based on this test on my M1 Macbook. Your result might vary.
mysql> select benchmark(100000000, left(year, 10)) from person;
+--------------------------------------+
| benchmark(100000000, left(year, 10)) |
+--------------------------------------+
| 0 |
+--------------------------------------+
1 row in set (1.75 sec)
mysql> select benchmark(100000000, date_format(year, '%Y-%m-%d')) from person;
+-----------------------------------------------------+
| benchmark(100000000, date_format(year, '%Y-%m-%d')) |
+-----------------------------------------------------+
| 0 |
+-----------------------------------------------------+
1 row in set (6.81 sec)
But given that I had to execute both expressions 100 million times to observe a significant difference, both of them are so fast that I wouldn't worry about it. It's likely that other parts of the query will be of far greater influence on performance.
Worrying about which of these two functions has better performance is like worrying if it's better to use one finger or two fingers to lift a 100kg barbell.

Why is MySQL ignoring the ORDER BY on this condition?

I want to show rows that have updated_at more than 3 hours ago. MySQL seems to be completely ignoring the ORDER BY clause. Any idea why?
Edit: as pointed out by Sebastian, this only occurs in certain timezones, like GMT+5 or GMT+8.
mysql> SET time_zone='+08:00';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE DATABASE test1; USE test1;
Query OK, 1 row affected (0.01 sec)
Database changed
mysql> CREATE TABLE `boxes` (
-> `box_id` int unsigned NOT NULL AUTO_INCREMENT,
-> `updated_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
-> PRIMARY KEY (`box_id`)
-> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO `boxes` (`box_id`, `updated_at`) VALUES
-> (1, '2020-08-22 05:25:35'),
-> (2, '2020-08-26 18:49:05'),
-> (3, '2020-08-23 03:28:30'),
-> (4, '2020-08-23 03:32:55');
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> SELECT NOW();
+---------------------+
| NOW() |
+---------------------+
| 2020-08-26 20:49:59 |
+---------------------+
1 row in set (0.00 sec)
mysql> SELECT b.box_id, updated_at, (b.updated_at < NOW() - INTERVAL 3 HOUR) AS more_than_3hr
-> FROM boxes b
-> ORDER BY more_than_3hr DESC;
+--------+---------------------+---------------+
| box_id | updated_at | more_than_3hr |
+--------+---------------------+---------------+
| 1 | 2020-08-22 05:25:35 | 1 |
| 2 | 2020-08-26 18:49:05 | 0 | <--- WHY IS THIS HERE???
| 3 | 2020-08-23 03:28:30 | 1 |
| 4 | 2020-08-23 03:32:55 | 1 |
+--------+---------------------+---------------+
4 rows in set (0.00 sec)
Expectation: the rows with "1" should show up first.
Actual results: ORDER BY is ignored, and the resultset is sorted by primary key
I have a hunch it has something to do with MySQL storing timestamps as UTC and displaying them in the current timezone. My current timezone is GMT+8. However, it still doesn't make sense -- I am sorting the results based on the aliased expression, and the expression's value is clearly shown in the resultset.
MySQL version 8.0.21.
I also tried moving the expression to the ORDER BY clause, and the results are the same.
I don't know why but it compares wrong timezones in the background and thus values at the end are correct, but comparisons are invalid (for specific timezones).
When you query a TIMESTAMP value, MySQL converts the UTC value back to
your connection’s time zone. Note that this conversion does not take
place for other temporal data types such as DATETIME.
https://www.mysqltutorial.org/mysql-timestamp.aspx/
Changing type from TIMESTAMP to DATETIME fixes problem.
Other solution may be casting to the decimal number.
SELECT b.box_id, updated_at, FORMAT((b.updated_at < NOW() - INTERVAL 3 HOUR),0) AS more_than_3hr
FROM boxes b
ORDER BY more_than_3hr DESC;
From the documentation:
https://dev.mysql.com/doc/refman/8.0/en/user-variables.html
HAVING, GROUP BY, and ORDER BY, when referring to a variable that is assigned a value in the select expression list do not work as expected because the expression is evaluated on the client and thus can use stale column values from a previous row.
Basically, you can't use a variable name you created with "AS" in your sorting.
The solution is to use the verbose statement you used for the AS in sorting. Yeah, it's verbose. 🤷‍♂️ It is what it is.

move decimals sql or divide

I built an administrative system in Laravel for my office, using mysql, one of the main tables is for the price of the products which is a double. The system has been in production for over 2 years now, but the government here changed our currency and removed 5 zeroes, so I need to change all the price values while keeping them with some decimals.
Currently, the lowest price in the list is 500
While the highest is 14985010
I need to perform a query to change ALL the price values in that column on the production DB (backup is done, so should be fine)
So the 500 one should be: 0.005
And the 14985010 should be: 149.8501
I'm thinking that all that is needed is to divide the values by 100000, but i might be wrong.
My SQL skills are super rusty, and I've been searching for a while but couldn't find the right answer. Any pointer would be greatly appreciated. Thanks!
I actually would recommend against just dividing all your currency data by some large factor, e.g. 100K. Instead, I propose adding a new column to your prices table, which is key into another table giving the factor which should be used to determine the current price. So you might have a setup like this:
prices table
price | factorKey
500 | 2
14985010 | 2
factors table
factorKey | value
1 | 1
2 | 0.00001
Then, to generate the actual current prices, you could do a join:
SELECT
p.price * f.value AS price
FROM prices p
INNER JOIN factors f
ON p.factorKey = f.factorKey;
Under this scheme, you would only need to modify the current factor to be used with your price data. You could even maintain another table to keep track of the historical prices. One reason to suggest this is that perhaps your government will change prices again in the future. It is error prone to do such large manual divisions/multiplications on your original data.
You should first fix the column to be sure it can handle the appropriate decimal places. I would suggest something like:
alter table t modify column price decimal(38, 10);
Then just update the value:
update t
price = price / 100000;
Having said that, you might want to check if the values are exactly what you expect:
select cast(cast(price as decimal(38, 10)) / 100000 as decimal(38, 10))
There may be subtle rounding errors that are not to your liking.
First of all, your currency data type should support decimal numbers, not just integers. I would suggest DECIMAL(12,4) for your column.
After you are sure the column data type can hold decimal numbers, you can update all rows in the table.
Here's a demonstration:
mysql> create table MyTable ( MyCurrencyColumn decimal(12,4));
Query OK, 0 rows affected (0.03 sec)
mysql> insert into MyTable values (500), (14985010);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from MyTable;
+------------------+
| MyCurrencyColumn |
+------------------+
| 500.0000 |
| 14985010.0000 |
+------------------+
2 rows in set (0.00 sec)
mysql> UPDATE MyTable SET MyCurrencyColumn = MyCurrencyColumn / 100000;
Query OK, 2 rows affected (0.02 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from MyTable;
+------------------+
| MyCurrencyColumn |
+------------------+
| 0.0050 |
| 149.8501 |
+------------------+

How to get Data Between Two Dates in mysql?

Table field structure:
id fdate tdate name
1 2012-07-07 2012-07-30
2 2012-08-07 2012-09-30
3 2012-07-17 2012-08-30
Here
fdate--->one of the field in table
tdate--->another field in table
I Will pass Two values like 2012-07-07 and 2012-09-30
And I need the output From the above Range:
You Have to Check the condition like the following fdate between2012-07-07and2012-09-30and tdate between2012-07-07and2012-09-30 like that
Expected Output:
month Days
07 43
08 53
09 30
Edit: Based on the authors comments the question is - How do I count all the days and show a total grouped by month when passing it a date range and comparing it to the data in my table.
I know that this doesn't give you the exact result that you want, but I think it will help you in the right direction at the very least:
mysql> create table dateTest (id int(2), fdate date, tdate date);
Query OK, 0 rows affected (0.04 sec)
mysql> insert into dateTest values(1, '2012-07-07', '2012-07-30');
Query OK, 1 row affected (0.00 sec)
mysql> insert into dateTest values(1, '2012-08-07', '2012-09-30');
Query OK, 1 row affected (0.00 sec)
mysql> insert into dateTest values(1, '2012-07-17', '2012-08-30');
Query OK, 1 row affected (0.00 sec)
mysql> select month(tdate) as month, datediff(tdate, fdate) as tally from dateTest group by month(fdate), month(tdate);
+-------+-------+
| month | tally |
+-------+-------+
| 7 | 23 |
| 8 | 44 |
| 9 | 54 |
+-------+-------+
3 rows in set (0.00 sec)
As you can see, it is skipping the middle month as I am grouping by fdate, but it is a step in the right direction for you.
Have a look at the TIMESTAMPDIFF() function in MySQL.
What this allows you to do is pass in two TIMESTAMP or DATETIME values (or even DATE as MySQL will auto-convert) as well as the unit of time you want to base your difference on.
You can specify MONTH as the unit in the first parameter:
mysql>SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-04')
-- Outputs: 1
mysql> SELECT TIMESTAMPDIFF(YEAR,'2002-05-01','2001-01-01');
-> -1
mysql> SELECT TIMESTAMPDIFF(MINUTE,'2003-02-01','2003-05-01 12:05:55');
-> 128885
Your question is not clear though. But I have this for you.
You need to fetch your data from MySQL table using php. Then you may calculate date difference there.
You may refer to this stackoverflow question How to calculate the difference between two dates using PHP? and the official php documentation about date_diff at http://php.net/manual/en/function.date-diff.php
You can use the between clause in my sql:
select * from tbl where datetime between '2012-07-07' and '2012-09-30';
as an example

Return Unique Dates in Range of Days

I am trying to query a table that is setup with id, startDateTime, endDateTime. Let's say row 1 looks like this:
id startDateTime endDateTime
100 2/9/2012 20:55 3/21/2012 10:43
I need to query the above such that I get a distinct count of all the days in the above range.
My expected result would be in the above 42 as there are 42 unique calendar days from 2/9 through 3/21. Datediff because it looks at the time piece gives me 41 days. I have tried various iterations of datediff and timediff trying to get this to work but can't find anything that works in all scenarios. Any suggestions as to how this can be done in SQL?
I started with a query as shown below:
SELECT ConditionStatus.ID,
SUM((DATEDIFF(ConditionStatus.endDate,ConditionStatus.startDate))) AS Duration
WHERE ID = 100
My query returns a Duration of 41 which is technically accurate but I need to condition such that every date in the range of dates gets a count of 1
I am trying to mimic some logic we use on our datawarehouse where we persist a count of 1 for each date for which there was activity.
Thanks,
Bob
The basic answer is that you can use DATEDIFF() and add 1 because you want to include the current day.
For example:
mysql> select datediff(current_date(),current_date()) + 1;
+---------------------------------------------+
| datediff(current_date(),current_date()) + 1 |
+---------------------------------------------+
| 1 |
+---------------------------------------------+
1 row in set (0.00 sec)
Expanding on your original example, you can convert the strings to datetimes, then discard the time component, then calculate the inclusive date count with a query like this:
mysql> SELECT ABS(DATEDIFF(DATE(STR_TO_DATE('2/9/2012 20:55','%m/%d/%Y %H:%i')),
-> DATE(STR_TO_DATE('3/21/2012 10:43','%m/%d/%Y %H:%i')))) + 1 as days_in_range;
+---------------+
| days_in_range |
+---------------+
| 42 |
+---------------+
1 row in set (0.00 sec)
mysql> SELECT ABS(DATEDIFF(DATE(STR_TO_DATE('3/21/2012 10:43','%m/%d/%Y %H:%i')),
-> DATE(STR_TO_DATE('2/9/2012 20:55','%m/%d/%Y %H:%i')))) + 1 as days_in_range;
+---------------+
| days_in_range |
+---------------+
| 42 |
+---------------+
1 row in set (0.00 sec)
See this answer - How do I select the number of distinct days in a date range?
Take a look before you use #Ike's answer. If you add +1, you will get one too many in instances where the time value is 00:00:00.
select datediff(days, '2/9/2012 20:55', '3/21/2012 10:43') returns 41
select datediff(days, '2/9/2012 20:55', '3/21/2012 10:43') + 1 returns 42
This is where that breaks -
select datediff(days, '2/9/2012 20:55', '3/21/2012 00:00:00') + 1 returns 42
That's the wrong answer. It should not include the last day.
Here's the workaround -
datediff(days, '2/9/2012 20:55', dateadd(seconds, -1, '3/21/2012 00:00:00') + 1