Symfony搭配Doctrine执行含MySQL自定义函数的迁移报语法错误
问题触发原因
DELIMITER是MySQL命令行客户端的专属指令,不属于MySQL服务端的标准SQL语法,作用是临时修改客户端的SQL语句分割符,避免存储过程/函数内部的;被客户端提前识别为语句结束标记。- 你通过Doctrine迁移执行SQL时,所有语句是直接通过DBAL层(底层是PDO)发送给MySQL服务端解析的,服务端无法识别
DELIMITER指令,所以直接抛出语法错误。 - 你在控制台执行成功是因为客户端先处理了
DELIMITER逻辑,把整个函数定义作为单条SQL发送给服务端,服务端只收到了CREATE FUNCTION的合法SQL,自然不会报错。
可行解决方案
方案1:移除DELIMITER相关代码,直接发送函数定义
Doctrine会把单个addSql调用的内容作为完整的单条SQL发送给服务端,不需要额外定义分隔符,直接把函数创建的核心逻辑放进去即可,修改后的迁移对应部分如下:
$this->addSql("DROP FUNCTION IF EXISTS wordcount;"); $this->addSql(" CREATE FUNCTION wordcount(str LONGTEXT) RETURNS INT DETERMINISTIC SQL SECURITY INVOKER NO SQL BEGIN DECLARE wordCnt, idx, maxIdx INT DEFAULT 0; DECLARE currChar, prevChar BOOL DEFAULT 0; SET maxIdx=char_length(str); SET idx = 1; WHILE idx <= maxIdx DO SET currChar=SUBSTRING(str, idx, 1) RLIKE '[[:alnum:]]'; IF NOT prevChar AND currChar THEN SET wordCnt=wordCnt+1; END IF; SET prevChar=currChar; SET idx=idx+1; END WHILE; RETURN wordCnt; END ");
该方案修改量最小,可保留你原有全部逻辑。
方案2:在PHP层计算字数(更推荐)
创建自定义MySQL函数需要数据库账号有CREATE ROUTINE权限,很多生产环境的数据库权限配置会禁止该权限,这种情况你可以直接用PHP处理字数统计:
public function up(Schema $schema) : void { $this->addSql('ALTER TABLE article ADD words_amount INT DEFAULT 0 NOT NULL'); // 遍历所有文章计算字数更新 $connection = $this->connection; $articles = $connection->fetchAllAssociative('SELECT id, content FROM article'); foreach ($articles as $article) { // 这里可以根据需求调整字数统计逻辑,比如适配中文、过滤HTML标签等 $wordCount = str_word_count(strip_tags($article['content'])); $connection->executeStatement( 'UPDATE article SET words_amount = ? WHERE id = ?', [$wordCount, $article['id']] ); } }
该方案兼容性更好,统计逻辑调整也更灵活,不需要依赖数据库的自定义函数权限。
内容的提问来源于stack exchange,提问作者Kaharon
相关产品推荐
相关产品推荐

