【问题标题】:python list in sql query as parameter [duplicate]sql查询中的python列表作为参数[重复]
【发布时间】:2010-09-21 23:16:37
【问题描述】:

我有一个 python 列表,比如 l

l = [1,5,8]

我想写一个 sql 查询来获取列表中所有元素的数据,比如说

select name from students where id = |IN THE LIST l|

我该如何做到这一点?

【问题讨论】:

    标签: python sql


    【解决方案1】:

    到目前为止,答案都是将值模板化为纯 SQL 字符串。这对整数来说绝对没问题,但如果我们想对字符串这样做,就会遇到转义问题。

    这是一个使用参数化查询的变体,适用于两者:

    placeholder= '?' # For SQLite. See DBAPI paramstyle.
    placeholders= ', '.join(placeholder for unused in l)
    query= 'SELECT name FROM students WHERE id IN (%s)' % placeholders
    cursor.execute(query, l)
    

    【讨论】:

    • ','.join(placeholder * len(l)) 会短一些,但仍然可读,恕我直言
    • @Thiefmaster:是的,一般情况下必须是([placeholder]*len(l)),因为占位符可能是多个字符。
    • @bobince 在我的情况下,我分配了一个变量 a='john' 和 b='snow' 并将其存储在一个名为 got=['a,b'] 的元组中并执行了同一个查询。这样做我得到了错误,即类型错误:在字符串格式化期间并非所有参数都转换了。我该怎么解决它
    • 为什么要“手动”加入而不是将这项工作留给 dbapi?关键是使用元组而不是列表...
    • 基于此答案,这里是 Python 3.6 cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l) 的单行解决方案。 @bobince,您还应该在您的解决方案中说明使用 ? 是避免 SQL 注入的安全方法。这里有很多易受攻击的答案,基本上任何在 python 中连接字符串的答案。
    【解决方案2】:

    最简单的方法是先将列表转到tuple

    t = tuple(l)
    query = "select name from studens where id IN {}".format(t)
    

    【讨论】:

    • 这其实是正确的答案。我不知道为什么它被忽略了。首先设置列表 l,然后使用元组,然后将元组传递给查询。干得好。
    • 正如@renstrm 所说,如果 l 只包含一个元素,这将不起作用,您最终会得到 id IN (1,)。这是语法错误。
    • @Amirhos Imani 如果你不知道你有多少元素怎么办?
    • 这与这里的大多数答案一样,除了接受的答案之外,容易受到 SQL 注入的影响,不应使用。
    • @BaconBits 公平警告,但对于 SQL 注入不是问题的某些应用程序(例如,分析我是唯一用户的数据库),这将是可行的。
    【解决方案3】:

    不要复杂化,解决方法很简单。

    l = [1,5,8]
    
    l = tuple(l)
    
    params = {'l': l}
    
    cursor.execute('SELECT * FROM table where id in %(l)s',params)
    

    希望对你有帮助!!!

    【讨论】:

    • 如果l 只包含一个元素,这将不起作用,您最终会得到id IN (1,)。这是语法错误。
    • 如果 l 只包含 1 个元素,请确保它是一个元组并且它会起作用。在我看来,这个解决方案比公认的解决方案更清晰。
    • 但是如果 tuple 为空还是不行,所以在查询执行前必须进行额外的检查。
    • 这对我来说不适用于sqlite3。您针对哪个库进行了测试?
    • 很好的解决方案,但@renstrm 和Никита-Конин 正确地要求对元组何时具有单个元素或没有元素进行额外检查。
    【解决方案4】:

    你想要的 SQL 是

    select name from studens where id in (1, 5, 8)
    

    如果你想从 python 构造这个,你可以使用

    l = [1, 5, 8]
    sql_query = 'select name from studens where id in (' + ','.join(map(str, l)) + ')'
    

    map 函数会将列表转换为可以使用str.join 方法通过逗号粘合在一起的字符串列表。

    或者:

    l = [1, 5, 8]
    sql_query = 'select name from studens where id in (' + ','.join((str(n) for n in l)) + ')'
    

    如果您更喜欢 generator expressions 而不是地图功能。

    更新:S. Lott 在 cmets 中提到 Python SQLite 绑定不支持序列。在这种情况下,您可能需要

    select name from studens where id = 1 or id = 5 or id = 8
    

    生成
    sql_query = 'select name from studens where ' + ' or '.join(('id = ' + str(n) for n in l))
    

    【讨论】:

    • :-( Python SQLite 绑定不支持序列。
    • 哦?我没有意识到。再说一次,我没有意识到我们要使用 SQLite 绑定解决方案。
    • 抱歉,没有暗示或暗示 SQLite —— 很遗憾列表的绑定在那里不起作用。
    【解决方案5】:

    string.join列表值以逗号分隔,并使用format operator组成查询字符串。

    myquery = "select name from studens where id in (%s)" % ",".join(map(str,mylist))
    

    (谢谢,blair-conrad

    【讨论】:

    • 由于这种方法不使用数据库参数替换,它会使您面临 SQL 注入攻击。这里使用的 % 只是普通的 Python 字符串格式。
    【解决方案6】:

    我喜欢 bobince 的回答:

    placeholder= '?' # For SQLite. See DBAPI paramstyle.
    placeholders= ', '.join(placeholder for unused in l)
    query= 'SELECT name FROM students WHERE id IN (%s)' % placeholders
    cursor.execute(query, l)
    

    但我注意到了这一点:

    placeholders= ', '.join(placeholder for unused in l)
    

    可以替换为:

    placeholders= ', '.join(placeholder*len(l))
    

    如果不那么聪明和不那么笼统,我觉得这更直接。这里l 需要有一个长度(即引用定义__len__ 方法的对象),这应该不是问题。但占位符也必须是单个字符。要支持多字符占位符使用:

    placeholders= ', '.join([placeholder]*len(l))
    

    【讨论】:

      【解决方案7】:

      如果您将 PostgreSQL 与 Psycopg2 库一起使用,您可以让其 tuple adaption 为您完成所有转义和字符串插值,例如:

      ids = [1,2,3]
      cur.execute(
        "SELECT * FROM foo WHERE id IN %s",
        [tuple(ids)])
      

      即只需确保您将IN 参数作为tuple 传递。如果是list,你可以使用= ANY array syntax

      cur.execute(
        "SELECT * FROM foo WHERE id = ANY (%s)",
        [list(ids)])
      

      请注意,这两者都会变成相同的查询计划,因此您应该使用更容易的那个。例如如果您的列表以元组形式出现,请使用前者,如果它们存储在列表中,请使用后者。

      【讨论】:

        【解决方案8】:

        只使用内联 if 操作与元组函数:

        query = "Select * from hr_employee WHERE id in " % tuple(employee_ids) if len(employee_ids) != 1 else "("+ str(employee_ids[0]) + ")"
        

        【讨论】:

        • 我的问题是参数化查询。注入的列表参数最终总是在生成的 SQL IN 子句中将其值括在引号中,这简直令人困惑。 tuple() 的想法奏效了。它之所以有效,是因为 DJango 将其转换为适合 SQL IN() 子句的字符串格式。当有几个答案建议使用tuple 时,这对我来说并没有点击。
        【解决方案9】:

        要从字段在字符串列表中的位置(而不是 int)运行选择,as per this question 使用 repr(tuple(map(str, l)))。完整示例:

        l = ['a','b','c']
        sql = f'''
        
        select name 
        from students 
        where id in {repr(tuple(map(str, l)))}
        '''
        print(sql)
        

        返回: select name from students where id in ('a', 'b', 'c')

        对于 Oracle 中的日期列表,这是可行的

        dates_str = ','.join([f'DATE {repr(s)}' for s in ['2020-11-24', '2020-12-28']])
        dates_str = f'({dates_str})'
        
        sql_cmd = f'''
        select *
        from students 
        where 
        and date in {dates_str}
        '''
        

        返回: select * from students where and date in (DATE '2020-11-24',DATE '2020-12-28')

        【讨论】:

        • 它解决了我在 python2.7 中的问题。非常感谢
        【解决方案10】:

        @umounted 答案的解决方案,因为它与单元素元组中断,因为 (1,) 不是有效的 SQL。:

        >>> random_ids = [1234,123,54,56,57,58,78,91]
        >>> cursor.execute("create table test (id)")
        >>> for item in random_ids:
            cursor.execute("insert into test values (%d)" % item)
        >>> sublist = [56,57,58]
        >>> cursor.execute("select id from test where id in %s" % str(tuple(sublist)).replace(',)',')'))
        >>> a = cursor.fetchall()
        >>> a
        [(56,), (57,), (58,)]
        

        sql字符串的其他解决方案:

        cursor.execute("select id from test where id in (%s)" % ('"'+'", "'.join(l)+'"'))
        

        【讨论】:

        • 由于sql注入,这不是更好的解决方案。
        【解决方案11】:
        placeholders= ', '.join("'{"+str(i)+"}'" for i in range(len(l)))
        query="select name from students where id (%s)"%placeholders
        query=query.format(*l)
        cursor.execute(query)
        

        这应该可以解决您的问题。

        【讨论】:

          【解决方案12】:

          一个更简单的解决方案:

          lst = [1,2,3,a,b,c]
          
          query = f"""SELECT * FROM table WHERE IN {str(lst)[1:-1}"""
          

          【讨论】:

            【解决方案13】:
            l = [1] # or [1,2,3]
            
            query = "SELECT * FROM table WHERE id IN :l"
            params = {'l' : tuple(l)}
            cursor.execute(query, params)
            

            :var 表示法似乎更简单。 (Python 3.7)

            【讨论】:

              【解决方案14】:

              例如,如果你想要sql查询:

              select name from studens where id in (1, 5, 8)
              

              怎么样:

              my_list = [1, 5, 8]
              cur.execute("select name from studens where id in %s" % repr(my_list).replace('[','(').replace(']',')') )
              

              【讨论】:

                【解决方案15】:

                这使用参数替换并处理单值列表的情况:

                l = [1,5,8]
                
                get_operator = lambda x: '=' if len(x) == 1 else 'IN'
                get_value = lambda x: int(x[0]) if len(x) == 1 else x
                
                query = 'SELECT * FROM table where id ' + get_operator(l) + ' %s'
                
                cursor.execute(query, (get_value(l),))
                

                【讨论】:

                  【解决方案16】:

                  如果列表中的值数等于 1 或大于 1,这将起作用

                  t = str(tuple(l))
                  if t[-2] == ',':
                     t= t.replace(t[-2],"")
                  query = "select name from studens where id IN {}".format(t)
                  

                  【讨论】:

                    猜你喜欢
                    • 2019-01-02
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-01-01
                    • 2021-02-25
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-04-30
                    相关资源
                    最近更新 更多