Find serial number with one character difference - mysql

We have a list of devices with serial numbers, human error has occurred and we have noticed some of them have 1 incorrect character. To find potential matches I've got this query:
Looking for serial 1234
SELECT * FROM tblDevices
WHERE serialNumber LIKE "123_"
OR serialNumber LIKE "12_4"
OR serialNumber LIKE "1_34"
OR serialNumber LIKE "_234"
This works and I can use PHP to incorporate all serial numbers, but this could become a lengthy query with some of the serial numbers containing 40 characters.
It also doesn't resolve if a user enters 2 characters wrong, but at least this narrows it down
I was wondering if there was a neater solution?
Thanks
EDIT
My php solution:
$serial = '123456789101112';
$query = "SELECT * FROM tblDevices WHERE ";
for($i=1; $i<=strlen($serial); $i++){
if ($i > 1){
$query .= " OR ";
}
$query .= "serialNumber LIKE '" . substr_replace($serial, "_", -$i, 1) . "'";
}
Gives me a the query but it's quite lengthy:
SELECT * FROM tblDevices
WHERE serialNumber LIKE '12345678910111_'
OR serialNumber LIKE '1234567891011_2'
OR serialNumber LIKE '123456789101_12'
OR serialNumber LIKE '12345678910_112'
OR serialNumber LIKE '1234567891_1112'
OR serialNumber LIKE '123456789_01112'
OR serialNumber LIKE '12345678_101112'
OR serialNumber LIKE '1234567_9101112'
OR serialNumber LIKE '123456_89101112'
OR serialNumber LIKE '12345_789101112'
OR serialNumber LIKE '1234_6789101112'
OR serialNumber LIKE '123_56789101112'
OR serialNumber LIKE '12_456789101112'
OR serialNumber LIKE '1_3456789101112'
OR serialNumber LIKE '_23456789101112'

In MySQL 8+ you can use a recursive CTE to generate the patterns and then join on them.
WITH RECURSIVE
n
AS
(
SELECT '1234' n
),
p
AS
(
SELECT n.n n,
0 l,
char_length(n.n) - 1 r,
concat('_', right(n.n, char_length(n.n) - 1)) p
FROM n
UNION ALL
SELECT p.n,
p.l + 1 l,
p.r - 1 r,
concat(left(p.n, p.l + 1), '_', right(p.n, p.r - 1)) p
FROM p
WHERE p.r - 1 >= 0
)
SELECT p.p,
d.*
FROM tbldevices d
INNER JOIN p
ON d.serialnumber LIKE p.p;
db<>fiddle

Related

MySQL - SELECT … WHERE id IN (SELECT ...)

I have the following query
SELECT * FROM table WHERE id IN (5,4,3,1,6)
and i want to get this value " 5,4,3,1,6 " from SELECT again.
SELECT * FROM table WHERE id IN (SELECT popuplarList FROM Settinng WHERE id =1 )
But i get only post number 5. I want to get all post from " 5,4,3,1,6 "
Is it possible to solve this with another logic or another way ?
thanks a lot
Try FIND_IN_SET Method
SELECT * FROM table WHERE FIND_IN_SET(id, (SELECT popuplarList FROM Settinng WHERE id =1 ))
Please try below:
1st solution:
select *
from table
where (
select CONCAT(",", popuplarList, ",")
from Settinng where id = 1
) like concat("%,", id, ",%")
2nd solution:
select * from table WHERE FIND_IN_SET(`id`, (
select CONCAT(",", popuplarList, ",") from Settinng where id = 1)
)

MySQL permutation

I have two tables. One has products and the other has bundles that go with it. I need to figure out the SQL that allows me to find all the combinations in which I can sell the product with extras.
Products
Name ID
Bench 1
Extra
Name ID Parent ID QTY
undershelf 1 1 1
overshelf 2 1 1
wheels 3 1 1
I need and output table that shows all the combination in which I can sell the product:
Bench
Bench + undershelf
Bench + undershelf + overshelf
Bench + overshelf
Bench + wheels
bench + wheels + overshelf and so one.
Every extras can be in the bundle or not, making that a binary property.
A way to visualize the combination is to create a word with a bit for every extra, 1 mean that the extra is in the list, 0 mean the that it is not.
For example Bench + undershelf + overshelf is 110 (or 011 if the binary string is read in the opposite order)
Generating every combination of n bit will give every combination of n extras, it will also give every number from 0 to 2^n - 1.
We can work back from here:
1. generate the list of number from 0 to 2^n - 1;
2. convert the number to binary, to list the combination of extras
3. match every bit with an extra
4. concatenate the names of the extras in the bundle description.
SELECT CONCAT(b.Name
, COALESCE(CONCAT(' + '
, GROUP_CONCAT(x.Name SEPARATOR ' + '))
, '')) Combination
FROM (SELECT p.Name, p.id
, LPAD(BIN(u.N + t.N * 10), e.Dim, '0') bitmap
FROM Products p
CROSS JOIN (SELECT 0 N UNION ALL SELECT 1
UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9) u
CROSS JOIN (SELECT 0 N UNION ALL SELECT 1
UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9) t
INNER JOIN (SELECT COUNT(1) Dim
, `Parent ID` pID
FROM Extra) E ON e.pID = p.ID
WHERE u.N + t.N * 10 < Pow(2, e.Dim)
) B
LEFT JOIN (SELECT #rownum := #rownum + 1 ID
, `Parent ID` pID
, Name
FROM Extra
, (Select #rownum := 0) r) X
ON x.pID = b.ID
AND SUBSTRING(b.bitmap, x.ID, 1) = '1'
GROUP BY b.Name, b.bitmap
this query will work up to six extras, then it'll need another digit table (one digit every three extras).
How it Works
The subquery E count the number of the extras, this is used in C to limit the elements generated by the digit tables u and t (unit and tens) to 2^dim.
The number is converted to binary by BIN(u.N + t.N * 10), then left padded with '0' to the number of elements, generating a combination bitmap.
To use the generated bitmap each extras need a fake id that will match a position in it, that's what the subquery X is meant for.
The two subqueries are JOINed by the nth char of the bitmap: if the char is 1 the extra is in the bundle, LEFT joined to not loose the product without extras.
I cannot think of any ingenious way of doing this in mysql, but it is very easy in a scripting language. Here in PHP:
<?php
$extra = array('undershelf', 'overshelf', 'sheels');
$possible_combinations = pow(2, count($extra));
for ($i = 0; $i < $possible_combinations; $i++) {
$combo = array('Bench');
foreach ($extra as $j => $item) {
if ($i & pow(2, $j)) {
$combo[] = $item;
}
}
echo implode(' + ', $combo) . "\n";
}
prints
Bench
Bench + undershelf
Bench + overshelf
Bench + undershelf + overshelf
Bench + sheels
Bench + undershelf + sheels
Bench + overshelf + sheels
Bench + undershelf + overshelf + sheels
Possible entirely within MySQL, though not simple. This example can handle up to 5 "extras", and is easily extensible for more:
CREATE TABLE products (name varchar(100), id int primary key);
INSERT INTO products (name, id) VALUES ('Bench', 1);
CREATE TABLE extra (name varchar(100), id int primary key, parent_id int references products.id, qty int);
INSERT INTO extra (name, id, parent_id, qty) VALUES
('undershelf', 1, 1, 1), ('overshelf', 2, 1, 1), ('wheels', 3, 1, 1);
CREATE TABLE boolean_values (x boolean);
INSERT INTO boolean_values VALUES (TRUE), (FALSE);
CREATE VIEW product_extras_interim_vw AS
SELECT p.id product_id, p.name product_name, e.id extra_id, e.name extra_name, x
FROM products p
JOIN extra e ON (e.parent_id = p.id)
CROSS JOIN boolean_values;
SELECT DISTINCT a.product_name
, CASE WHEN a.x THEN CONCAT(' + ', a.extra_name) END extra1
, CASE WHEN b.x THEN CONCAT(' + ', b.extra_name) END extra2
, CASE WHEN c.x THEN CONCAT(' + ', c.extra_name) END extra3
, CASE WHEN d.x THEN CONCAT(' + ', d.extra_name) END extra4
, CASE WHEN e.x THEN CONCAT(' + ', e.extra_name) END extra5
FROM product_extras_interim_vw a
LEFT JOIN product_extras_interim_vw b
ON ( a.product_id = b.product_id
AND b.extra_id > a.extra_id
AND a.x )
LEFT JOIN product_extras_interim_vw c
ON ( a.product_id = c.product_id
AND c.extra_id > b.extra_id
AND b.x )
LEFT JOIN product_extras_interim_vw d
ON ( a.product_id = d.product_id
AND d.extra_id > c.extra_id
AND c.x)
LEFT JOIN product_extras_interim_vw e
ON ( a.product_id = e.product_id
AND e.extra_id > d.extra_id
AND d.x)
ORDER BY product_name, extra1, extra2, extra3, extra4, extra5;
Output:
Bench
Bench + overshelf
Bench + overshelf + wheels
Bench + undershelf
Bench + undershelf + overshelf
Bench + undershelf + overshelf + wheels
Bench + undershelf + wheels
Bench + wheels

How do I determine the difference between values in database column and return the smallest difference?

I've got a column of DATETIME values in MySQL and I'm looking to return the smallest difference between any two of those values in the column; I don't need to know what values the difference was between or when it was, just the difference between these two values alone.
My table looks similar to this.
id | launch_date_time
----------------------------
1 | 2012-01-02 18:42:00
2 | 2012-04-05 07:23:50
...
x | 2014-08-07 22:19:11
Would anyone be able to point me in the correct direction for constructing such a query?
My first idea is, if your table name is table do this
select min( abs( datediff(t1.launch_date_time, t2.launch_date_time) ) )
from table t1
, table t2
where t1.id <> t2.id
it depends how big those tables are, the above is O(N^2) solution, in O(N * log N) you can do this by sorting and result is min of consecutive elements
// pseudo code
list = fromDb();
sort(list);
min = list[1] - list[0];
for i in 2 to list.size()
min = min( min, list[i] - list[i-1] )
select id,FROM_UNIXTIME(launch_date_time) as uTime,max(FROM_UNIXTIME(launch_date_time)) as max from table;
//do db stuff
//set the lowest to highest possible number so first comparison it will become the lowest.
$lowest = $result['max'];
// iterate through array to get each time
foreach ($result['uTime'] as $v)
{
//iterate again to compare each time u got from first loop to each in this loop
foreach ($result['uTime as $x)
{
//subtract your first value from the current
$diff = $v - $x;
//if its lower then any previous, and its positive set the new lowest.
if ($diff < $lowest and $diff > 0)
{
$lowest = $diff;
}
}
}
echo $lowest;
havent tested this....
SQL method of doing it in a single statement (difference here in days):-
SELECT MIN(DATEDIFF(Sub1.launch_date_time, Sub2.launch_date_time))
FROM
(
SELECT id, launch_date_time, #aCnt1 := #aCnt1 + 1 AS SeqCnt
FROM SomeTable
CROSS JOIN
(
SELECT #aCnt1:=0
) Deriv1
ORDER BY launch_date_time
) Sub1
INNER JOIN
(
SELECT id, launch_date_time, #aCnt2 := #aCnt2 + 1 AS SeqCnt
FROM SomeTable
CROSS JOIN
(
SELECT #aCnt2:=1
) Deriv2
ORDER BY launch_date_time
) Sub2
ON Sub1.SeqCnt = Sub2.SeqCnt
SQL Fiddle here:-
http://www.sqlfiddle.com/#!2/6dc399/1
Try this query -
SELECT
t1.launch_date_time, MIN(t2.launch_date_time) launch_date_time2
FROM launch t1
JOIN launch t2
ON t2.launch_date_time > t1.launch_date_time
GROUP BY
t1.launch_date_time
ORDER BY
DATE(MIN(t2.launch_date_time)) * 86400 + TIME_TO_SEC(TIME(MIN(t2.launch_date_time))) - DATE(t1.launch_date_time) * 86400 + TIME_TO_SEC(TIME(t1.launch_date_time))
LIMIT 1

mysql sorting cells with letters and words

I got cells that might have the following data (number of errors)
1
2
3
PASS
NoFileFound
NoLog
99
10
2
I would like sort with ascending order and descending order where I would PASS to be treated as a value of 0 and any other text based value should be treated as value of 1 error. As of now, these cells are stored as 'text' in the mysql database. How can this be done for MYSQL? What changes do I need to do?
Try this one:
SELECT Number FROM (
SELECT IF(valueField='PASS',0,1) as Number FROM TableMix
WHERE concat('',valueField * 1) <> valueField ) A
UNION ALL
SELECT Number FROM (
SELECT CAST(valueField as UNSIGNED) as Number FROM TableMix
WHERE concat('',valueField * 1) = valueField ) B
ORDER BY Number
See my SqlFiddle Demo
And this one with original values included:
SELECT Number, valueField FROM (
SELECT IF(valueField='PASS',0,1) as Number, valueField FROM TableMix
WHERE concat('',valueField * 1) <> valueField ) A
UNION ALL
SELECT Number, valueField FROM (
SELECT CAST(valueField as UNSIGNED) as Number, valueField FROM TableMix
WHERE concat('',valueField * 1) = valueField ) B
ORDER BY Number
See this Demo.

Get random record in a set of results

I have a simple MySQL query like this:
SELECT * ,
( MATCH (table.get) AGAINST('playstation ' IN BOOLEAN MODE) )
+ ( table.get LIKE '%playstation%') AS _score
FROM table
JOIN users on table.id_user = users.id
WHERE table.expire_datetime > 1375997618
HAVING _score > 0
ORDER BY RAND(table.id) ,_score DESC ;
If I run this query in MySQL, it returns usually more then 1 record, now I would like to LIMIT 1 and get one of them randomly, not always the same record.
Is it possible?
select * from <my_table>
ORDER BY RAND()
LIMIT 4
You would quit seeding the random number generator. My guess is that it is returning the first table id encountered, so the numbers are generated in the same sequence:
SELECT * ,
( MATCH (table.get) AGAINST('playstation ' IN BOOLEAN MODE) )
+ ( table.get LIKE '%playstation%') AS _score
FROM table
JOIN users on table.id_user = users.id
WHERE table.expire_datetime > 1375997618
HAVING _score > 0
ORDER BY RAND()
LIMIT 1;
As I understand problem in ,_score ?
Try this:
Select * FROM (
***your sql query***
) as t
ORDER BY RAND()
LIMIT 1