Autor Zpráva
slon_cz
Profil
Zdravím, řekněme, že mám číslo 4754 a potřeboval bych z databáze najít všechna čísla, která jsou blízko tomuto číslu, tedy 4854, 3754 atp. v rozmezí max jednohoo čísla pod a nad tím jedním číslem, je to vůbec možné? Zkoušel jsem '475_%' OR kod LIKE '47_4%' OR kod LIKE '4_54%' OR kod LIKE '_754%' jenže to není moc dobré, potřeboval bych aby to bylo vždy v rozmezí jednoho čísla z těch 4 v řadě. Nějaké nápady na takový regex?
Kcko
Profil
slon_cz:
Tak si to slož v PHPku a pak si to dosad jako sloupec IN (tvoje hodnoty)
slon_cz
Profil
Ano, v php by to šlo, jen jsem myslel, že by to šlo nějak přímo v sql příkazu přímo :)
Keeehi
Profil
Bohužel jsi nenapsal, jak se to má chovat v případě rozdílně dlouhých čísel. Jestli to chceš brát spíše jako čísla a doplnit to zleva nulami, nebo to chceš brát více jako řetězce a považuješ to za prefix. Podle toho se pak řeší porovnání. V prvním případě by vzdálenost 1 od čísla 4754 mělo číslo 754, v druhém 475. V mé implementaci je použita první možnost.

DROP FUNCTION IF EXISTS hamming_distance;
DELIMITER $$
CREATE FUNCTION `hamming_distance`( i1 INT, i2 INT) RETURNS INT(11)
    DETERMINISTIC
BEGIN 
    DECLARE s1_len, s2_len, i, l, r, d1, d2 INT;
    DECLARE s1, s2 TEXT;
    
    SET s1 = CONVERT(i1, char), s2 = CONVERT(i2, char); 
    SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), i = 1, d1 = 0, d2 = 0;

    IF s1_len < s2_len THEN
      SET l = s1_len, r = s2_len - s1_len, d2 = s2_len - s1_len;
    ELSE
      SET l = s2_len, r = s1_len - s2_len, d1 = s1_len - s2_len;
    END IF;
    
    WHILE i <= l DO
      IF SUBSTRING(s1, i + d1, 1) != SUBSTRING(s2, i + d2, 1) THEN
        SET r = r + 1;
      END IF;
      SET i = i + 1;
    END WHILE;
    
    RETURN r; 
  END;
$$

DELIMITER ;

SELECT * FROM table WHERE hamming_distance(4754, column) = 1;
slon_cz
Profil
Zdravím, to číslo je vždy 4-místné. Při pokusu mi to ovšem hodí chybu

Chyba v dotazu (1366): Incorrect integer value: '' for column 'i2' at row 47

DROP FUNCTION IF EXISTS hamming_distance;
DELIMITER $$
CREATE FUNCTION `hamming_distance`( i1 INT, i2 INT) RETURNS INT(11)
    DETERMINISTIC
BEGIN 
    DECLARE s1_len, s2_len, i, l, r, d1, d2 INT;
    DECLARE s1, s2 TEXT;
    
    SET s1 = CONVERT(i1, char), s2 = CONVERT(i2, char); 
    SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), i = 1, d1 = 0, d2 = 0;
 
    IF s1_len < s2_len THEN
      SET l = s1_len, r = s2_len - s1_len, d2 = s2_len - s1_len;
    ELSE
      SET l = s2_len, r = s1_len - s2_len, d1 = s1_len - s2_len;
    END IF;
    
    WHILE i <= l DO
      IF SUBSTRING(s1, i + d1, 1) != SUBSTRING(s2, i + d2, 1) THEN
        SET r = r + 1;
      END IF;
      SET i = i + 1;
    END WHILE;
    
    RETURN r; 
  END;
$$
 
DELIMITER ;
 
SELECT * FROM tabulka WHERE hamming_distance(4754, kod) = 1;
Keeehi
Profil
slon_cz:
to číslo je vždy 4-místné
V tom případě ta funkce může být jednodušší

DROP FUNCTION IF EXISTS hamming_distance;
DELIMITER $$
CREATE FUNCTION `hamming_distance`( i1 INT, i2 INT) RETURNS INT(11)
    DETERMINISTIC
BEGIN 
    DECLARE  i, r INT;
    DECLARE s1, s2 TEXT;
    
    SET s1 = CONVERT(i1, char), s2 = CONVERT(i2, char), i = 1, r = 0;
 
    WHILE i <= 4 DO
      IF SUBSTRING(s1, i, 1) != SUBSTRING(s2, i, 1) THEN
        SET r = r + 1;
      END IF;
      SET i = i + 1;
    END WHILE;
    
    RETURN r; 
  END;
$$
 
DELIMITER ;

Chyba v dotazu (1366): Incorrect integer value: '' for column 'i2' at row 47
To vypadá, jako kdyby jsi ve sloupci kod měl nějakou prázdnou hodnotu -> pak to číslo není vždy 4-místné ale občas 0-místné. Navíc to taky vypadá, že datový typ sloupce kod není číselný ale textový. Pokud se jedná o text, je zbytečné pak uvnitř funkce dělat konverzi.

DROP FUNCTION IF EXISTS hamming_distance;
DELIMITER $$
CREATE FUNCTION `hamming_distance`( s1 TEXT, s2 TEXT) RETURNS INT(11)
    DETERMINISTIC
BEGIN 
    DECLARE  i, r INT;
    SET i = 1, r = 0;
 
    WHILE i <= 4 DO
      IF SUBSTRING(s1, i, 1) != SUBSTRING(s2, i, 1) THEN
        SET r = r + 1;
      END IF;
      SET i = i + 1;
    END WHILE;
    
    RETURN r; 
  END;
$$
 
DELIMITER ;

SELECT * FROM tabulka WHERE hamming_distance('4754', kod) = 1;
funkce nepočítá s tou prázdnou hodnotou, samozřejmě by se to dalo ošetřit
Kajman
Profil
slon_cz:
v rozmezí max jednohoo čísla pod a nad tím jedním číslem

Rychlejší bude si v php sestavit těch max. 8 řetězců, kterých se to týká, jak radil Kcko. Databáze pak může využít případný index nad tím sloupcem.

select * from `tabulka` where `kod` in ('3754', '4654', '4744', '4753', '4755', '4764', '4854', '5754')
Kcko
Profil
Ano přesně tak, nehledě na to, že to v PHP vrstvě zvládneš spíš než modifikovat Keehiho funkci, která vypadá o dost složitěji na pochopení i modifikaci než pár řádků php kódu a roli tam bude hrát samozřejmě i ta rychlost.

Vaše odpověď


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: