Переменные MYSQL - SET @var - PullRequest
1 голос
/ 13 мая 2010

Я пытаюсь создать фрагмент MySQL, который будет анализировать таблицу и удалять дублирующиеся записи (дубликаты основаны на двух полях, а не на всей записи)

У меня есть следующий код, который работает, когда я жестко кодирую переменные в запросах, но когда я вынимаю их и ставлю их как переменные, я получаю ошибки MySQL, ниже приведен скрипт:

SET @tblname = 'mytable';
SET @fieldname = 'myfield';
SET @concat1 = 'checkfield1';
SET @concat2 = 'checkfield2';

ALTER TABLE @tblname ADD `tmpcheck` VARCHAR( 255 ) NOT NULL;

UPDATE @tblname SET `tmpcheck` = CONCAT(@concat1,'-',@concat2);

CREATE TEMPORARY TABLE `tmp_table` (
`tmpfield` VARCHAR( 100 ) NOT NULL
) ENGINE = MYISAM ;

INSERT INTO `tmp_table` (`tmpfield`) SELECT @fieldname FROM @tblname GROUP BY `tmpcheck` HAVING ( COUNT(`tmpcheck`) > 1 );

DELETE FROM @tblname WHERE @fieldname IN (SELECT `tmpfield` FROM `tmp_table`);

ALTER TABLE @tblname DROP `tmpcheck`;

Я получаю следующую ошибку:

#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@tblname ADD `tmpcheck` VARCHAR( 255 ) NOT NULL' at line 1 

Это потому, что я не могу использовать переменную для имени таблицы? Что еще может быть не так или как мне обойти эту проблему.

Ответы [ 3 ]

2 голосов
/ 13 мая 2010

Это потому, что я не могу использовать переменную для имени таблицы?

Да, или для других имен схем, таких как столбцы. Строковые переменные можно использовать только в том случае, если MySQL ожидает строку, заключенную в кавычки '.

Если вам действительно нужно это сделать, вы можете с помощью «динамического SQL»: создать весь запрос в виде строки, объединить @tblname в строку в то время и выполнить лот с помощью EXECUTE, Это довольно уродливо и может привести к SQL-инъекции, если вы не будете осторожны, поэтому избегайте его, если есть какая-либо другая опция.

ВЫБРАТЬ myfield ИЗ mytable GROUP BY tmpcheck ИМЕТЬ (COUNT (tmpcheck)> 1)

Это кажется мне проблематичным. Если myfield не имеет функциональной зависимости от tmpcheck (какой AFAICS он не может, поскольку tmpcheck не является первичным ключом), это недопустимый ANSI SQL. MySQL позволил бы вам сойти с рук, но вы бы сказали, что «для каждой группы строк, имеющих значение tmpcheck, выберите fieldname из одной строки из этой группы в случайном порядке для последующего удаления ». Это действительно то, что вы хотите? Я ожидаю, что вы захотите удалить всех, кроме одного дубликатов.

Обычно для удаления дубликатов такая сложная процедура не требуется. Просто используйте DELETE-join:

DELETE my0
FROM mytable AS my0
JOIN mytable AS my1
    ON my1.checkfield1=my0.checkfield1 AND my1.checkfield2=my0.checkfield2
    AND my1.id>my0.id;

Предполагается, что поле id является упорядоченным и UNIQUE, так что вы можете решить, какой ряд будет оставаться (здесь, с самым высоким id). myfield может быть этим полем, но я не могу понять из контекста.

1 голос
/ 13 мая 2010

Использование переменной для имени таблицы действительно недопустимо. Вам нужно будет сгенерировать SQL как строку и использовать подготовленную инструкцию для ее выполнения.

0 голосов
/ 13 мая 2010

Я использовал комбинацию обоих ответов:

SET @tblname = 'myTable';
SET @idfield = 'myPrimaryKey';
SET @check1 = 'field1';
SET @check2 = 'field2';

SET @q1 = CONCAT('DELETE my0 FROM `',@tblname, '` AS my0 JOIN `',@tblname, '` AS my1 ON my1.',@check1,' = my0.',@check1,' AND my1.',@check2,' = my0.',@check2,' AND my1.',@idfield,' > my0.',@idfield,'');
PREPARE stmt1 FROM @q1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
...