psycopg2混用%s变量与LIKE 'fake_%'触发IndexError问题求助
解决psycopg2中变量占位符与LIKE通配符冲突的问题
问题原因
psycopg2会将SQL中的单个%识别为参数占位符,当你在LIKE条件中写'fake_%'时,后面的%会被误判为需要绑定的变量,导致参数数量不匹配,抛出IndexError: tuple index out of range。
解决方案
方法一:转义LIKE中的通配符
将LIKE条件里的单个%替换为%%,psycopg2会自动将%%解析为SQL中的单个%通配符,不会当成参数占位符。修改后的SQL如下:
SELECT a.trade_date, a.ticker, a.company_name, a.cusip, a.shares_held, a.nominal, a.weighting, b.weighting "previous_weighting", ABS(a.weighting - b.weighting) "weighting_change" FROM t_ark_holdings a LEFT JOIN t_ark_holdings b ON a.etf_ticker=b.etf_ticker AND a.ticker=b.ticker AND b.trade_date=(SELECT MAX(trade_date) FROM t_ark_holdings WHERE trade_date<a.trade_date) WHERE a.etf_ticker = %s AND LOWER(a.ticker) NOT LIKE 'fake_%%' AND a.weighting<>b.weighting AND a.trade_date = (SELECT MAX(trade_date) FROM t_ark_holdings) ORDER BY a.trade_date DESC, "weighting_change" DESC, a.ticker
执行时只需传入一个参数即可,和之前的调用方式一致。
方法二:使用AsIs封装固定条件
如果不想修改SQL中的%,可以将LIKE条件作为参数传入,并用psycopg2.extensions.AsIs封装,让psycopg2直接将其作为SQL的一部分处理,不进行参数绑定:
from psycopg2.extensions import AsIs # 原SQL保留'fake_%',将LIKE条件设为占位符 query = """ SELECT a.trade_date, a.ticker, a.company_name, a.cusip, a.shares_held, a.nominal, a.weighting, b.weighting "previous_weighting", ABS(a.weighting - b.weighting) "weighting_change" FROM t_ark_holdings a LEFT JOIN t_ark_holdings b ON a.etf_ticker=b.etf_ticker AND a.ticker=b.ticker AND b.trade_date=(SELECT MAX(trade_date) FROM t_ark_holdings WHERE trade_date<a.trade_date) WHERE a.etf_ticker = %s AND LOWER(a.ticker) NOT LIKE %s AND a.weighting<>b.weighting AND a.trade_date = (SELECT MAX(trade_date) FROM t_ark_holdings) ORDER BY a.trade_date DESC, "weighting_change" DESC, a.ticker """ # 传入参数时用AsIs封装固定条件 cursor.execute(query, (your_etf_ticker_value, AsIs("'fake_%'")))
方法三:用Django ORM重构查询(推荐)
直接使用Django的ORM构建查询,框架会自动处理参数绑定和SQL转义,彻底避免原生SQL的占位符冲突问题,同时更符合Django开发规范:
假设对应模型为ArkHolding:
from django.db.models import Max, F, Abs from django.db.models import Subquery, OuterRef # 获取最新交易日 latest_date = ArkHolding.objects.aggregate(max_date=Max('trade_date'))['max_date'] # 子查询:获取每个标的上一交易日的权重 prev_weight_subquery = ArkHolding.objects.filter( etf_ticker=OuterRef('etf_ticker'), ticker=OuterRef('ticker'), trade_date__lt=OuterRef('trade_date') ).order_by('-trade_date').values('weighting')[:1] # 构建最终查询 results = ArkHolding.objects.filter( etf_ticker=your_etf_ticker_value, ticker__lower__not_like='fake_%', trade_date=latest_date, weighting__ne=Subquery(prev_weight_subquery) ).annotate( previous_weighting=Subquery(prev_weight_subquery), weighting_change=Abs(F('weighting') - F('previous_weighting')) ).order_by('-trade_date', '-weighting_change', 'ticker').values( 'trade_date', 'ticker', 'company_name', 'cusip', 'shares_held', 'nominal', 'weighting', 'previous_weighting', 'weighting_change' )
执行后results即为所需的查询结果,无需手动处理SQL和参数绑定。
内容的提问来源于stack exchange,提问作者Je Je
相关产品推荐
相关产品推荐

