As I am new to MySQL, this question may be silly. How can I find the difference between successive rows?
Example:
A table (tableName='Abc') contains a single row as follows,
|DATA|
|10 |
|20 |
|30 |
|40 |
|50 |
Here I want get the output as,
|Dif|
|10 |
|10 |
|10 |
|10 |
How to find the difference like this without any index (primary or Auto_increment)?
A self-join is one way to compare consecutive rows:
SELECT
MIN(T2.DATA - T1.DATA) AS Dif
FROM
Abc T1 INNER JOIN Abc T2 on T1.DATA < T2.DATA
ORDER BY
T1.DATA
Use user-defined variables:
SET #prevValue:=0;
SELECT value-#prevValue AS diff,#prevValue:=value FROM t;
You'll have to discard the first row for your case.
If the table contains both value and diff fields, you can set the diff field with:
SET #prevValue:=0;
UPDATE t SET diff=IF((#d:=value-#prevValue) AND (#prevValue:=value),#d,#d);
Using the IF is the only way I could find to both set diff and update the #prevValue variable.
It would be best to do this outside of the DB by keeping track of the previous row. Here's some code (hopefully my php is not too rusty):
<?php
$prevNum = 0;
$db = mysql_connect(...); // fill in connection string
mysql_select_db("my_db", $db);
$result = mysql_query("select DATA from Abc order by DATA");
while ($row = mysql_fetch_array($result)) {
$diff = $row[0] - $prevNum;
echo "$diff\n";
$prevNum = $row[0];
}
?>
If you need to do it within the db for some reason then it would probably be best to create a stored procedure which will basically do the same thing: instantiate a variable with value 0, report the difference for each row and that variable, then set the variable to the row value.
Edited to add an order by clause as noted by John Pick
Related
I have two tables something like this:
Table 1:
+---------------------+
| name_fr | name_en |
+---------------------+
| valfr1 | valen1 |
+---------------------+
Table 2:
+------------------------+
| id | value |
+------------------------+
| 1 | valfr1 is thiss |
+------------------------+
| 2 | something random |
+------------------------+
I try to loop each row of table 1 and take the values of each field, then for each row in table 2 I intend to do a replacement in the value field. Given the tables example, the loop would do something like this:
update table2 set value = replace(value, 'valfr1', 'valen1');
And it would replace the value in table2 with id 1 and it will have 'valen1 is thiss'
But imagine table1 has for example 100 rows, how can i loop for each of them and try to replace the value?
Thanks for the help, sorry if i couldn't explain myself correctly
Introduction
You can easily achieve something like this with an update command. Don't worry, that's ultimately does a loop under the hood, it's just a looping that has been ever optimizing for decades, so it's probable that your loop will not perform as well as that. At least not without a very large amount of effort. So, for this answer I will assume that an update is good-enough for this purpose.
Reference: https://www.mysqltutorial.org/mysql-update-join/
The query
UPDATE TABLE1
JOIN TABLE2
ON TABLE1.value LIKE CONCAT('%', TABLE2.name_fr, '%')
SET TABLE1.value = REPLACE(TABLE1.value, TABLE2.name_fr, TABLE2.name_en);
Explanation
This query matches all records from TABLE1 to their counterparts from TABLE2, where TABLE1.value contains TABLE2.name_fr. For these matches the replacement is done for TABLE1.value accordingly to the mapping specified in TABLE2.
Edge-case
If there is a name_fr value which contains another, then it is better to evaluate the former first, because the latter could make premature replacements if evaluated first. For this purpose you could order TABLE2 descendingly by fr_name length and alias is to some name.
I've got (e.g.) the following MySQL row:
firstname | lastname | anotherCol1 | anotherCol2 | anotherCol3
--------------------------------------------------------------
John | Smith | Football | Fast cars | 18 Fools
and for instance the following strings:
oh, xxx, foo, 41
I am looking for a statement that returns oh and foo, because the rows cells do contain these substrings (oh: John, foo: Football & Fools).
A possible solution
SELECT q.keyword
FROM Table1 t CROSS JOIN
(
SELECT 'oh' keyword UNION ALL
SELECT 'xxx' UNION ALL
SELECT 'foo' UNION ALL
SELECT '41'
) q
WHERE INSTR(CONCAT_WS(' ', firstname,
lastname,
anotherCol1,
anotherCol2), q.keyword) > 0
Output:
| KEYWORD |
-----------
| oh |
| foo |
Here is SQLFiddle demo
If your table is MyISAM, you can make use of Full-Text Search
Create an index on your table with the following command:
ALTER TABLE <TABLE_NAME> ADD FULLTEXT(firstname, lastname, anotherCol1, anotherCol2, anotherCol3 );
Now, run your queries (Use boolean mode as you have to match on multiple keywords):
SELECT * FROM <TABLE_NAME> WHERE MATCH(firstname, lastname, anotherCol1, anotherCol2, anotherCol3) AGAINST ('+oh +xxx +foo +41' IN BOOLEAN MODE );
Hope this helps.
It's mainly a question of whom you want to take the brunt of the workload.
In my solution, I'll opt for PHP taking it, simply because a database is for querying data, but not for calculating or processing it.
Now, first things first, you'll fetch all data from your database.
SELECT * FROM table;
Nothing more, nothing less. This will return you every single column and every single row in the table. Now for the PHP part.
<?php
while($row = mysqli_fetch_assoc($result)) {
foreach($row as $header => $col) {
foreach($strings as $str) {
if(strstr($col, $str) !== false) {
echo "$str found in column $header";
}
}
}
}
?>
I hope this helps a little.
I have a MySQL database who contains a table named images. In that table I have a column named file_name. The file_name column contains 5 rows:
.---.-----------.
| # | file_name |
.---.-----------.
| 1 | 00000 |
| 2 | 00001 |
| 3 | 00002 |
| 4 | 00003 |
| 5 | 00004 |
.---------------.
My query code is:
<?php
require_once('config/config_db.php');
$SQLquery = "SELECT `file_name` FROM images";
$result = mysql_query($SQLquery);
while($row = mysql_fetch_array($result)){
echo $row['file_name']."<br>"; // ?
}
?>
I have the following PHP function who generates a 5 digit random function, but my exeptions are introduced manually in $exception variable array (as you can see below). My goal is to get from file_name column all rows, and set in $exception variable. I tried to achieve this 3 or 4 hours, but no success. Thank you for your time.
The PHP function:
<?php
function getRandom($exception=array('00000', '00001', '00002', '00003', '00004')){ // ?
$rand = str_pad(mt_rand(0,99999),5,0,STR_PAD_LEFT);
if(in_array($rand,$exception))return getRandom($exception);
return $rand;
}
echo getRandom().'<br>';
?>
Based on my earlier question, this might do the job for you in a single SQL statement:-
SELECT aNumber
FROM (
SELECT LPAD(CONVERT(a.i+b.i*10+c.i*100+d.i*1000+e.i*10000,CHAR(5)),5,'0') AS aNumber
FROM integers a, integers b, integers c, integers d, integers e) Sub1
LEFT OUTER JOIN images b ON Sub1.aNumber = b.file_name
WHERE b.file_name IS NULL
ORDER BY RAND()
LIMIT 1
Uses a table called integers with a single column called i and 10 rows with the values 0 to 9.
It is using ORDER BY RAND() which is a bit slow so probably worth you optimising that a bit.
I use Perl and DBI to manage my MySQL tables, querys, etc. How can I show the running time of a query?
If I do a SELECT in the console, the result will be like this:
+-----+-------------+
| id | name |
+-----+--------------
| 1 | Jack |
| 2 | Joe |
| 3 | Mary |
+-----+-------------+
3 rows in set (0.17 sec)
I need to show 0.17 sec. There is any way in DBI to show the running time in Perl, something like this?
my $dbh = $db->prepare("SELECT id, name FROM names ORDER BY id;");
$dbh->execute;
print $dbh->runnin_time; # ???
DBI#Profile, DBI::Profile, DBI::ProfileData, DBI::ProfileDumper, dbiprof
You take a timestamp before you run the query, and a timestamp after. The difference is your query execution time. For obtaining high-resolution timestamps, see Time::HiRes
I can't find anything in DBI. I think that there is nothing already implemented out of the box, though could be interesting information.
The other way to do this would be to get the time before and after the execution and then make a simple difference. You can do it from within your Perl script simply getting the time stamp before the query execution, and after, then subtract the two to find the execution time.
my $start = DateTime->now;
my $dbh = $db->prepare("SELECT id, name FROM names ORDER BY id;");
$dbh->execute;
my $end = DateTime->now;
my $elapsedtime = ($end->subtract_datetime($start))->seconds;
print "Execution time(seconds) : $elapsedtime \n";
Using Time::HiRes could also be an easy way to find the query time. Here is an example that uses Time::HiRes:
use Time::HiRes;
$start_time = Time::HiRes::gettimeofday();
my $dbh = $db->prepare("SELECT id, name FROM names ORDER BY id;");
$dbh->execute;
$end_time = Time::HiRes::gettimeofday();
my $elapsedtime = sprintf("%.6f", $end_time - $start_time);
print "Execution time(seconds) : $elapsedtime \n";
Reading #daxim's links to documentation, there is a simple way to achieve this by running your script with DBI_PROFILE=2 which is from DBI::Profile
Example output:
DBI::Profile: 53.203692s 50.67% (6725 calls) script.pl # 2016-01-21 11:51:49
'INSERT INTO FOO ("BAR") VALUES (?)' =>
0.057596s / 2 = 0.028798s avg (first 0.051621s, min 0.005975s, max 0.051621s)
'INSERT INTO BAZ ("QUX") VALUES (?)' =>
0.367184s / 44 = 0.008345s avg (first 0.039410s, min 0.002445s, max 0.039410s)
I want to use field name to determine the column that I will use to search for, but first I have to use the name to look up a column ID, then concatenate the column ID with a string to get the actual column name. I'm doing with with two queries now, but I'd like to combine them into a single query.
For example, I want to search for "fool=true". In the first table, I find out that values for "fool" are in column 1 of the second table. In the second table, I do the real search, for "field_id_1=true".
This table maps names to column IDs used in the second table.
exp_channel_fields
+field_id +field_name +
|---------|-----------|
|1 |fool |
|2 |money |
+---------+-----------|
This table has the data I want to search, but the column names don't quite match the ID from the prior step. I have to prepend "field_id_" to the ID I found before:
exp_channel_data
+entry_id+field_id_1 +field_id_2 +
|--------|-----------|-----------|
|837 |true |$500 |
|838 |false |$50,000 |
+--------+-----------+-----------+
Here's the first query I use, and I store the result into $myFieldID.
SELECT field_id as tField_id FROM exp_channel_fields
WHERE field_name = 'fool'
Then I use the result to build the second query:
SELECT
GROUP_CONCAT(entry_id ORDER BY entry_id SEPARATOR '|')
AS result_entries
FROM exp_channel_data
WHERE field_id_{$myFieldID} = "true"
Is there a way to combine these into a single query? Thanks in advance!
it is not possible to use query-results as column names, but you can use server-side prepared statements to achieve a similar behavior. have a look at this question:
MySQL concat() to create column names to be used in a query?
Your database is not right normalized. You should unpivot columns to rows in order to make this kind of querys:
exp_channel_fields
+field_id +field_name +
|---------|-----------|
|1 |fool |
|2 |money |
+---------+-----------|
exp_channel_data
+entry_id+field_id + value
|--------|-----------|-----------|
|837 |1 |true |
|837 |2 |$500 |
|838 |1 |False |
|838 |2 |$50,000 |
+--------+-----------+-----------+
Then the query is really easy:
SELECT field_id as tField_id FROM exp_channel_fields
WHERE field_name = 'fool'
SELECT
GROUP_CONCAT(entry_id ORDER BY entry_id SEPARATOR '|')
AS result_entries
FROM
exp_channel_data inner join
exp_channel_fields
on exp_channel_data.field_id = exp_channel_data.field_id
WHERE value = "true"
Perhaps you will need table-base and sub-tables for exp_channel_fields because differents value types.