Сравнение чисел с плавающей запятой зависит от контекста. Поскольку даже изменение порядка операций может приводить к различным результатам, важно знать, как «равно» вы хотите, чтобы числа были.
Сравнение чисел с плавающей запятой Брюса Доусона хорошее место для начала при сравнении с плавающей точкой.
Следующие определения взяты из . Искусство компьютерного программирования Кнутом :
bool approximatelyEqual(float a, float b, float epsilon)
{
return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}
bool essentiallyEqual(float a, float b, float epsilon)
{
return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}
bool definitelyGreaterThan(float a, float b, float epsilon)
{
return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}
bool definitelyLessThan(float a, float b, float epsilon)
{
return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}
Конечно, выбор epsilon зависит от контекста и определяет, как вы хотите, чтобы номера были.
Другим методом сравнения чисел с плавающей запятой является просмотр ULP (единиц в последнем месте) чисел. Хотя речь не идет конкретно о сравнении, статья Что каждый компьютерный ученый должен знать о числах с плавающей запятой , является хорошим ресурсом для понимания того, как работает точка с плавающей запятой, и каковы подводные камни, в том числе и то, что представляет собой ULP.
Основываясь на решении Альвина Кеслера, вот пример более практичного реального мира.
Предполагая, что список, разделенный запятыми, находится в my_table.list, и это список идентификаторов для my_other_table.id, вы можете сделать что-то вроде:
SELECT
*
FROM
my_other_table
WHERE
(SELECT list FROM my_table WHERE id = '1234') REGEXP CONCAT(',?', my_other_table.id, ',?');
Я решил эту проблему с регулярным шаблоном выражения. Они, как правило, медленнее, чем обычные запросы, но это простой способ получить данные в столбце запроса с разделителями-запятыми
SELECT *
FROM `TABLE`
WHERE `field` REGEXP ',?[SEARCHED-VALUE],?';
, жадный вопросительный знак помогает искать в начале или в конце строки.
Надеюсь, что это поможет любому в будущем
У меня была аналогичная проблема с полем, подобным тому, который я решил по-другому. Моему варианту использования нужно было использовать эти идентификаторы в списке, разделенном запятыми, для использования в соединении.
Я смог решить его с помощью подобных, но это стало проще, потому что в дополнение к разделителю запятой Иды также цитировались так:
keys
"1","2","6","12"
Из-за этого я смог сделать LIKE
SELECT twwf.id, jtwi.id joined_id
FROM table_with_weird_field twwf
INNER JOIN join_table_with_ids jtwi
ON twwf.delimited_field LIKE CONCAT("%\"", jtwi.id, "\"%")
В основном это просто посмотреть, будет ли идентификатор из таблицы, в которую вы пытаетесь присоединиться, появится в наборе, и в этот момент вы можете легко присоединиться к нему и вернуть свои записи. Вы также можете просто создать представление из чего-то вроде этого.
Это сработало для моего случая использования, когда я имел дело с плагином Wordpress, который управлял отношениями в описанном выше порядке. Кавычки действительно помогают, потому что иначе вы рискуете частичными совпадениями (aka - id 1 в течение 18 и т. Д.).
Вот что я получил до сих пор (нашел его на странице Ben Alpert ):
SELECT REPLACE(
SUBSTRING(
SUBSTRING_INDEX(c.`courseNames`, ',', e.`courseId` + 1)
, LENGTH(SUBSTRING_INDEX(c.`courseNames`, ',', e.`courseId`)
) + 1)
, ','
, ''
)
FROM `clients` c INNER JOIN `clientenrols` e USING (`clientId`)
[[:<:]]word[[:>:]]
. В противном случае из значения "WEB" он может извлекать более одного значения - например, E или B или EB или WE (любая комбинация).
– DarkSide
27 March 2014 в 18:36
SELECT
tab1.std_name, tab1.stdCode, tab1.payment,
SUBSTRING_INDEX(tab1.payment, '|', 1) as rupees,
SUBSTRING(tab1.payment, LENGTH(SUBSTRING_INDEX(tab1.payment, '|', 1)) + 2,LENGTH(SUBSTRING_INDEX(tab1.payment, '|', 2))) as date
FROM (
SELECT DISTINCT
si.std_name, hfc.stdCode,
if(isnull(hfc.payDate), concat(hfc.coutionMoneyIn,'|', year(hfc.startDtae), '-', monthname(hfc.startDtae)), concat(hfc.payMoney, '|', monthname(hfc.payDate), '-', year(hfc.payDate))) AS payment
FROM hostelfeescollection hfc
INNER JOIN hostelfeecollectmode hfm ON hfc.tranId = hfm.tranId
INNER JOIN student_info_1 si ON si.std_code = hfc.stdCode
WHERE hfc.tranId = 'TRAN-AZZZY69454'
) AS tab1
Увидев, что это довольно популярный вопрос - ответ ДА.
Для столбца column
в таблице table
, содержащего все ваши данные, разделенные комой:
CREATE TEMPORARY TABLE temp (val CHAR(255));
SET @S1 = CONCAT("INSERT INTO temp (val) VALUES ('",REPLACE((SELECT GROUP_CONCAT( DISTINCT `column`) AS data FROM `table`), ",", "'),('"),"');");
PREPARE stmt1 FROM @s1;
EXECUTE stmt1;
SELECT DISTINCT(val) FROM temp;
< hr> Пожалуйста, помните, однако, чтобы не хранить CSV в вашей БД
Per @Mark Amery - так как это переводит значения, разделенные комой, в инструкцию INSERT
, будьте осторожны при запуске ее на неанитированной data
Просто повторите, пожалуйста, не храните CSV в своей БД; эта функция предназначена для преобразования CSV в разумную структуру БД и не использоваться в любом месте вашего кода. Если вам нужно использовать его в производстве, переосмыслите структуру своего БД
SET @S1 = CONCAT("INSERT INTO temp (val) VALUES ('",REPLACE((SELECT GROUP_CONCAT( DISTINCT `column`) AS data FROM `table`), "|", "'),('"),"');");
– eithed
18 May 2016 в 13:55
table
содержат кавычки, возможно, подвергая вас инъекциям SQL.
– Mark Amery
1 April 2017 в 18:40
Вот как вы это делаете для SQL Server. Кто-то еще может перевести его в MySQL. Разбор CSV-значений в несколько строк .
SELECT Author,
NullIf(SubString(',' + Phrase + ',' , ID , CharIndex(',' , ',' + Phrase + ',' , ID) - ID) , '') AS Word
FROM Tally, Quotes
WHERE ID <= Len(',' + Phrase + ',') AND SubString(',' + Phrase + ',' , ID - 1, 1) = ','
AND CharIndex(',' , ',' + Phrase + ',' , ID) - ID > 0
Идея состоит в том, чтобы перекрестно присоединиться к предопределенной таблице Tally, которая содержит целое число от 1 до 8000 (или сколько-нибудь большое количество) и запустить SubString
, чтобы найти право, слово, позицию.
Ну, я ничего не использовал, поэтому решил создать реальную простую функцию разделения, надеюсь, что это поможет:
DECLARE inipos INTEGER;
DECLARE endpos INTEGER;
DECLARE maxlen INTEGER;
DECLARE item VARCHAR(100);
DECLARE delim VARCHAR(1);
SET delim = '|';
SET inipos = 1;
SET fullstr = CONCAT(fullstr, delim);
SET maxlen = LENGTH(fullstr);
REPEAT
SET endpos = LOCATE(delim, fullstr, inipos);
SET item = SUBSTR(fullstr, inipos, endpos - inipos);
IF item <> '' AND item IS NOT NULL THEN
USE_THE_ITEM_STRING;
END IF;
SET inipos = endpos + 1;
UNTIL inipos >= maxlen END REPEAT;
Вы можете создать для этого функцию:
/**
* Split a string by string (Similar to the php function explode())
*
* @param VARCHAR(12) delim The boundary string (delimiter).
* @param VARCHAR(255) str The input string.
* @param INT pos The index of the string to return
* @return VARCHAR(255) The (pos)th substring
* @return VARCHAR(255) Returns the [pos]th string created by splitting the str parameter on boundaries formed by the delimiter.
* @{@example
* SELECT SPLIT_STRING('|', 'one|two|three|four', 1);
* This query
* }
*/
DROP FUNCTION IF EXISTS SPLIT_STRING;
CREATE FUNCTION SPLIT_STRING(delim VARCHAR(12), str VARCHAR(255), pos INT)
RETURNS VARCHAR(255) DETERMINISTIC
RETURN
REPLACE(
SUBSTRING(
SUBSTRING_INDEX(str, delim, pos),
LENGTH(SUBSTRING_INDEX(str, delim, pos-1)) + 1
),
delim, ''
);
Преобразование магического псевдокода для его использования: у вас есть:
SELECT e.`studentId`, SPLIT_STRING(',', c.`courseNames`, e.`courseId`)
FROM...
До сих пор я хотел сохранить эти разделенные запятыми списки в своем SQL-db - хорошо осведомленный обо всех предупреждениях!
Я все думал о том, что у них есть преимущества по сравнению с поисковыми таблицами (которые обеспечивают способ нормализации база данных). После нескольких дней отказа я увидел свет:
Короче говоря, есть причина, по которой нет родной SPLIT () в MySQL.
Можно взорвать строку в инструкции MySQL SELECT.
Сначала сгенерируйте ряд чисел с наибольшим количеством разграниченных значений, которые вы хотите взорвать. Либо из таблицы целых чисел, либо путем объединения чисел вместе. Следующее генерирует 100 строк, давая значения от 1 до 100. Его можно легко расширить, чтобы получить большие диапазоны (добавьте еще один дополнительный запрос, дающий значения от 0 до 9 для сотен - отсюда давая от 0 до 999 и т. Д.).
SELECT 1 + units.i + tens.i * 10 AS aNum
FROM (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
Это может быть крест, соединенный с вашей таблицей, чтобы дать вам значения. Обратите внимание, что вы используете SUBSTRING_INDEX, чтобы получить значение с разделителем до определенного значения, а затем используйте SUBSTRING_INDEX для получения этого значения, за исключением предыдущих.
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(clients.courseNames, ',', sub0.aNum), ',', -1) AS a_course_name
FROM clients
CROSS JOIN
(
SELECT 1 + units.i + tens.i * 10 AS aNum, units.i + tens.i * 10 AS aSubscript
FROM (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
) sub0
Как вы можете видеть, здесь есть небольшая проблема, последнее разделимое значение повторяется много раз. Чтобы избавиться от этого, вам нужно ограничить диапазон чисел в зависимости от количества разделителей. Это можно сделать, взяв длину поля с разделителями и сравнивая его с длиной поля с разделителями, когда разделители изменены на «» (чтобы удалить их). Из этого вы можете получить количество разделителей: -
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(clients.courseNames, ',', sub0.aNum), ',', -1) AS a_course_name
FROM clients
INNER JOIN
(
SELECT 1 + units.i + tens.i * 10 AS aNum
FROM (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
) sub0
ON (1 + LENGTH(clients.courseNames) - LENGTH(REPLACE(clients.courseNames, ',', ''))) >= sub0.aNum
В исходном поле примера вы могли (например) подсчитать количество студентов на каждом курсе на основе этого. Обратите внимание, что я изменил sub-запрос, который получает диапазон чисел, чтобы вернуть 2 числа, 1 используется для определения имени курса (поскольку они основаны на старте на 1), а другой получает индекс (поскольку они основаны на запуске на 0).
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(clients.courseNames, ',', sub0.aNum), ',', -1) AS a_course_name, COUNT(clientenrols.studentId)
FROM clients
INNER JOIN
(
SELECT 1 + units.i + tens.i * 10 AS aNum, units.i + tens.i * 10 AS aSubscript
FROM (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
) sub0
ON (1 + LENGTH(clients.courseNames) - LENGTH(REPLACE(clients.courseNames, ',', ''))) >= sub0.aNum
LEFT OUTER JOIN clientenrols
ON clientenrols.courseId = sub0.aSubscript
GROUP BY a_course_name
Как вы можете видеть, это возможно, но довольно грязно. И с небольшой возможностью использовать индексы он не будет эффективным. Далее диапазон должен справляться с наибольшим количеством разделимых значений и работает, исключая множество дубликатов; если максимальное количество разделимых значений очень велико, это значительно замедлит работу. В целом, как правило, гораздо лучше правильно нормализовать базу данных.
Есть более простой способ, есть таблица ссылок, например:
Таблица 1: клиенты, информация о клиенте, бла-бла-бла
Таблица 2: курсы, информация о курсе, бла-бла
Таблица 3: clientid, courseid
Затем выполните JOIN, и вы отправитесь на гонки.
Я использовал вышеупомянутую логику, но немного изменил ее. Мой вход имеет формат: «apple: 100 | pinapple: 200 | orange: 300», хранящийся в переменной @updtAdvanceKeyVal
Вот функциональный блок:
set @res = "";
set @i = 1;
set @updtAdvanceKeyVal = updtAdvanceKeyVal;
REPEAT
-- set r = replace(SUBSTRING(SUBSTRING_INDEX(@updtAdvanceKeyVal, "|", @i),
-- LENGTH(SUBSTRING_INDEX(@updtAdvanceKeyVal, "|", @i -1)) + 1),"|","");
-- wrapping the function in "replace" function as above causes to cut off a character from
-- the 2nd splitted value if the value is more than 3 characters. Writing it in 2 lines causes no such problem and the output is as expected
-- sample output by executing the above function :
-- orange:100
-- pi apple:200 !!!!!!!!strange output!!!!!!!!
-- tomato:500
set @r = SUBSTRING(SUBSTRING_INDEX(@updtAdvanceKeyVal, "|", @i),
LENGTH(SUBSTRING_INDEX(@updtAdvanceKeyVal, "|", @i -1)) + 1);
set @r = replace(@r,"|","");
if @r <> "" then
set @key = SUBSTRING_INDEX(@r, ":",1);
set @val = SUBSTRING_INDEX(@r, ":",-1);
select @key, @val;
end if;
set @i = @i + 1;
until @r = ""
END REPEAT;
Если вам нужно получить таблицу из строки с разделителями:
SET @str = 'function1;function2;function3;function4;aaa;bbbb;nnnnn';
SET @delimeter = ';';
SET @sql_statement = CONCAT('SELECT '''
,REPLACE(@str, @delimeter, ''' UNION ALL SELECT ''')
,'''');
SELECT @sql_statement;
SELECT 'function1' UNION ALL SELECT 'function2' UNION ALL SELECT 'function3' UNION ALL SELECT 'function4' UNION ALL SELECT 'aaa' UNION ALL SELECT 'bbbb' UNION ALL SELECT 'nnnnn'
$("form").validate({
submitHandler: function (form) {
console.log('test');
}
});
. В противном случае из значения & quot; WEB & quot; он может извлечь более одного значения - например, E или B или EB или WE (любая комбинация)
– DarkSide
27 March 2014 в 18:36