{"id":10280,"date":"2026-04-07T08:12:05","date_gmt":"2026-04-07T16:12:05","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/python\/?p=10280"},"modified":"2026-04-07T08:12:05","modified_gmt":"2026-04-07T16:12:05","slug":"write-sql-your-way-dual-parameter-style-benefits-in-mssql-python","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/python\/write-sql-your-way-dual-parameter-style-benefits-in-mssql-python\/","title":{"rendered":"Write SQL Your Way: Dual Parameter Style Benefits in mssql-python"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/python\/wp-content\/uploads\/sites\/12\/2025\/09\/Python_SQL_img.png\"><img decoding=\"async\" class=\" wp-image-10181 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/python\/wp-content\/uploads\/sites\/12\/2025\/09\/Python_SQL_img-300x152.png\" alt=\"Python SQL img image\" width=\"1074\" height=\"544\" srcset=\"https:\/\/devblogs.microsoft.com\/python\/wp-content\/uploads\/sites\/12\/2025\/09\/Python_SQL_img-300x152.png 300w, https:\/\/devblogs.microsoft.com\/python\/wp-content\/uploads\/sites\/12\/2025\/09\/Python_SQL_img-768x389.png 768w, https:\/\/devblogs.microsoft.com\/python\/wp-content\/uploads\/sites\/12\/2025\/09\/Python_SQL_img.png 1024w\" sizes=\"(max-width: 1074px) 100vw, 1074px\" \/><\/a><\/p>\n<p><span class=\"TextRun SCXW11324769 BCX8\" lang=\"EN-IN\" xml:lang=\"EN-IN\" data-contrast=\"none\"><span class=\"NormalTextRun CommentStart SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\"><em>Reviewed by: Sumit Sarabhai<\/em><\/span><\/span><\/p>\n<p>If\u00a0<span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">you&#8217;ve<\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">\u00a0been writing SQL in Python, you already know the debate: <strong>positional parameters (?)<\/strong> or <strong>named parameters (%(name)s)<\/strong>? Some developers swear by the conciseness of positional. Others prefer the clarity of named. With <strong><a id=\"menurrmp\" class=\"fui-Link ___1q1shib f2hkw1w f3rmtva f1ewtqcl fyind8e f1k6fduh f1w7gpdv fk6fouc fjoy568 figsok6 f1s184ao f1mk8lai fnbmjn9 f1o700av f13mvf36 f1cmlufx f9n3di6 f1ids18y f1tx3yz7 f1deo86v f1eh06m1 f1iescvh fhgqx19 f1olyrje f1p93eir f1nev41a f1h8hb77 f1lqvz6u f10aw75t fsle3fq f17ae5zn\" title=\"https:\/\/github.com\/microsoft\/mssql-python\" href=\"https:\/\/github.com\/microsoft\/mssql-python\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Link mssql-python\u00a0\">mssql-python<\/a><\/strong><\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">, you no longer need to choose\u00a0<\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">&#8211;<\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">\u00a0we support\u00a0<\/span><span class=\"NormalTextRun CommentStart SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">both<\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">.<\/span><span class=\"LineBreakBlob BlobObject DragDrop SCXW11324769 BCX8\"><span class=\"SCXW11324769 BCX8\">\u00a0<\/span><br class=\"SCXW11324769 BCX8\" \/><\/span><span class=\"LineBreakBlob BlobObject DragDrop SCXW11324769 BCX8\"><span class=\"SCXW11324769 BCX8\">\u00a0<\/span><br class=\"SCXW11324769 BCX8\" \/><\/span><span class=\"TextRun SCXW11324769 BCX8\" lang=\"EN-IN\" xml:lang=\"EN-IN\" data-contrast=\"none\"><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">We&#8217;ve added dual parameter style support to\u00a0<\/span><span class=\"NormalTextRun SpellingErrorV2Themed SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">mssql<\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">-python, enabling both\u00a0<\/span><span class=\"NormalTextRun SpellingErrorV2Themed SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">qmark<\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">\u00a0and\u00a0<\/span><span class=\"NormalTextRun SpellingErrorV2Themed SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">pyformat<\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">\u00a0parameter styles in Python applications that interact with SQL Server and Azure SQL<\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">. This feature is especially useful if\u00a0<\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">you\u2019re<\/span><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">\u00a0building complex queries, dynamically assembling filters, or migrating existing code that already uses named parameters with other DB<\/span><\/span><span class=\"TextRun SCXW11324769 BCX8\" lang=\"EN-IN\" xml:lang=\"EN-IN\" data-contrast=\"none\"><span class=\"NormalTextRun SCXW11324769 BCX8\" data-ccp-parastyle=\"heading 2\">API drivers.\n<\/span><\/span><\/p>\n<p><div class=\"alert alert-info\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>Try it here<\/strong><\/p> You can install driver using <a id=\"menurrq4\" class=\"fui-Link ___1q1shib f2hkw1w f3rmtva f1ewtqcl fyind8e f1k6fduh f1w7gpdv fk6fouc fjoy568 figsok6 f1s184ao f1mk8lai fnbmjn9 f1o700av f13mvf36 f1cmlufx f9n3di6 f1ids18y f1tx3yz7 f1deo86v f1eh06m1 f1iescvh fhgqx19 f1olyrje f1p93eir f1nev41a f1h8hb77 f1lqvz6u f10aw75t fsle3fq f17ae5zn\" title=\"https:\/\/github.com\/microsoft\/mssql-python\" href=\"https:\/\/pypi.org\/project\/mssql-python\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Link pip install mssql-python\"><strong>pip install mssql-python<\/strong><\/a><\/p>\n<p>Calling all Python + SQL developers! We invite the community to try out mssql-python and help us shape the future of high-performance <a id=\"menurrq6\" class=\"fui-Link ___1q1shib f2hkw1w f3rmtva f1ewtqcl fyind8e f1k6fduh f1w7gpdv fk6fouc fjoy568 figsok6 f1s184ao f1mk8lai fnbmjn9 f1o700av f13mvf36 f1cmlufx f9n3di6 f1ids18y f1tx3yz7 f1deo86v f1eh06m1 f1iescvh fhgqx19 f1olyrje f1p93eir f1nev41a f1h8hb77 f1lqvz6u f10aw75t fsle3fq f17ae5zn\" title=\"https:\/\/github.com\/microsoft\/mssql-python\" href=\"https:\/\/github.com\/microsoft\/mssql-python\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Link SQL Server connectivity in Python\">SQL Server connectivity in Python<\/a>.!<\/div><\/p>\n<p data-ccp-border-top=\"0px none \" data-ccp-padding-top=\"0px\" aria-level=\"2\"><span style=\"font-size: 18pt;\"><b>What Are Parameter Styles?<\/b><\/span><span data-ccp-props=\"{&quot;134233118&quot;:true,&quot;335559738&quot;:450,&quot;335572079&quot;:12,&quot;335572080&quot;:6,&quot;335572081&quot;:10921365,&quot;469789806&quot;:&quot;single&quot;}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">The\u00a0<\/span><a href=\"https:\/\/peps.python.org\/pep-0249\/#paramstyle\"><span data-contrast=\"none\">DB-API 2.0 specification (PEP 249)<\/span><\/a><span data-contrast=\"none\">\u00a0defines several ways to pass parameters to SQL queries. The two most popular are:<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/p>\n<ul>\n<li aria-setsize=\"-1\" data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"5\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><b><span data-contrast=\"none\">qmark<\/span><\/b><span data-contrast=\"none\">\u00a0&#8211;\u00a0Positional\u00a0<\/span><span data-contrast=\"none\">?<\/span><span data-contrast=\"none\">\u00a0placeholders with a tuple\/list of values.<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<li aria-setsize=\"-1\" data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"5\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><b><span data-contrast=\"none\">pyformat<\/span><\/b><span data-contrast=\"none\">\u00a0&#8211;\u00a0Named\u00a0<\/span><span data-contrast=\"none\">%(name)s<\/span><span data-contrast=\"none\">\u00a0placeholders with a dictionary of values.<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\"><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\n<\/span><\/span><\/p>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\"># qmark style \r\ncursor.execute(\"SELECT * FROM users WHERE id = ? AND status = ?\", (42, \"active\")) \r\n \r\n# pyformat style \r\ncursor.execute(\"SELECT * FROM users WHERE id = %(id)s AND status = %(status)s\", \r\n               {\"id\": 42, \"status\": \"active\"}) <\/code><\/pre>\n<\/li>\n<\/ul>\n<p data-ccp-border-top=\"0px none \" data-ccp-padding-top=\"0px\" aria-level=\"2\"><span style=\"font-size: 18pt;\"><b>Business\u00a0Requirement<\/b>\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">Previously,\u00a0<\/span><span data-contrast=\"none\">mssql-python<\/span><span data-contrast=\"none\">\u00a0only supported\u00a0<\/span><span data-contrast=\"none\">qmark<\/span><span data-contrast=\"none\">. It works fine for simple queries, but as parameters multiply, tracking their order becomes error-prone:<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/p>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\"># Which ? corresponds to which value? \r\ncursor.execute( \r\n    \"UPDATE users SET name=?, email=?, age=? WHERE id=? AND status=?\", \r\n    (name, email, age, user_id, status) \r\n) <\/code><\/pre>\n<p><span data-contrast=\"none\">Mix up the order and\u00a0it\u2019s\u00a0easy to introduce subtle,\u00a0hard <\/span><span data-contrast=\"none\">to <\/span><span data-contrast=\"none\">spot\u00a0bugs.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/p>\n<p><span style=\"font-size: 18pt;\"><b>Why Named Parameters?<\/b><\/span><span data-ccp-props=\"{&quot;134233118&quot;:true,&quot;335559738&quot;:450,&quot;335572079&quot;:12,&quot;335572080&quot;:6,&quot;335572081&quot;:10921365,&quot;469789806&quot;:&quot;single&quot;}\">\u00a0<\/span><\/p>\n<ul>\n<li><b><span data-contrast=\"none\">Self-documenting queries<\/span><\/b><span data-contrast=\"none\">\u00a0&#8211;\u00a0No more guessing\u00a0which\u00a0<\/span><span data-contrast=\"none\">?<\/span><span data-contrast=\"none\">\u00a0maps to what:<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/li>\n<\/ul>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">qmark \u2014 6 parameters, which is which?\u00a0\r\ncursor.execute( \"\"\"INSERT INTO employees (first_name, last_name, email, department, salary, hire_date) VALUES (?, ?, ?, ?, ?, ?)\"\"\", (\"Jane\", \"Doe\", \"jane.doe@company.com\", \"Engineering\", 95000, \"2025-03-01\") )\u00a0\r\npyformat \u2014 every value is labeled\u00a0\r\ncursor.execute( \"\"\"INSERT INTO employees (first_name, last_name, email, department, salary, hire_date) VALUES (%(first_name)s, %(last_name)s, %(email)s, %(dept)s, %(salary)s, %(hire_date)s)\"\"\", {\"first_name\": \"Jane\", \"last_name\": \"Doe\", \"email\": \"jane.doe@company.com\", \"dept\": \"Engineering\", \"salary\": 95000, \"hire_date\": \"2025-03-01\"} ) <\/code><\/pre>\n<ul>\n<li><b><span data-contrast=\"none\">Parameter reuse<\/span><\/b><span data-contrast=\"none\">\u00a0&#8211;\u00a0Use the same value multiple times without repeating it:<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/li>\n<\/ul>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">Audit log: record who made the change and when\u00a0\r\ncursor.execute( \"\"\"UPDATE orders SET status = %(new_status)s, modified_by = %(user)s, approved_by = %(user)s, modified_at = %(now)s, approved_at = %(now)s WHERE order_id = %(order_id)s\"\"\", {\"new_status\": \"approved\", \"user\": \"admin@company.com\", \"now\": datetime.now(), \"order_id\": 5042} )\u00a0\r\n3 unique values, used 5 times \u2014 no duplication needed <\/code><\/pre>\n<ul>\n<li><b><span data-contrast=\"none\">Dynamic query building<\/span><\/b><span data-contrast=\"none\">\u00a0&#8211;\u00a0Add filters without tracking parameter positions:<\/span><\/li>\n<\/ul>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">def search_orders(customer=None, status=None, min_total=None, date_from=None): \r\n    query_parts = [\"SELECT * FROM orders WHERE 1=1\"] \r\n    params = {} \r\n  \r\n    if customer: \r\n        query_parts.append(\"AND customer_id = %(customer)s\") \r\n        params[\"customer\"] = customer \r\n  \r\n    if status: \r\n        query_parts.append(\"AND status = %(status)s\") \r\n        params[\"status\"] = status \r\n  \r\n    if min_total is not None: \r\n        query_parts.append(\"AND total &gt;= %(min_total)s\") \r\n        params[\"min_total\"] = min_total \r\n  \r\n    if date_from: \r\n        query_parts.append(\"AND order_date &gt;= %(date_from)s\") \r\n        params[\"date_from\"] = date_from \r\n  \r\n    query_parts.append(\"ORDER BY order_date DESC\") \r\n    cursor.execute(\" \".join(query_parts), params) \r\n    return cursor.fetchall() \r\n  \r\n# Callers use only the filters they need \r\nrecent_big_orders = search_orders(min_total=500, date_from=\"2025-01-01\") \r\npending_for_alice = search_orders(customer=42, status=\"pending\") <\/code><\/pre>\n<ul>\n<li><b><span data-contrast=\"none\">Dictionary Reuse Across Queries<\/span><\/b><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/li>\n<\/ul>\n<p><span data-contrast=\"none\">The same parameter dictionary can drive multiple queries:<\/span><\/p>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">report_params = {\"region\": \"West\", \"year\": 2025, \"status\": \"active\"} \r\n  \r\n# Summary count \r\ncursor.execute( \r\n    \"\"\"SELECT COUNT(*) FROM customers \r\n       WHERE region = %(region)s AND status = %(status)s\"\"\", \r\n    report_params \r\n) \r\ntotal = cursor.fetchone()[0] \r\n  \r\n# Revenue breakdown \r\ncursor.execute( \r\n    \"\"\"SELECT department, SUM(revenue) \r\n       FROM sales \r\n       WHERE region = %(region)s AND fiscal_year = %(year)s \r\n       GROUP BY department \r\n       ORDER BY SUM(revenue) DESC\"\"\", \r\n    report_params \r\n) \r\nbreakdown = cursor.fetchall() \r\n  \r\n# Top performers \r\ncursor.execute( \r\n    \"\"\"SELECT name, revenue \r\n       FROM sales_reps \r\n       WHERE region = %(region)s AND fiscal_year = %(year)s AND status = %(status)s \r\n       ORDER BY revenue DESC\"\"\", \r\n    report_params \r\n) \r\ntop_reps = cursor.fetchall() \r\n# Same dict, three different queries \u2014 change the filters once, all queries update <\/code><\/pre>\n<p aria-level=\"2\"><span style=\"font-size: 18pt;\"><b>The Solution: Automatic Detection<\/b><\/span><span data-ccp-props=\"{&quot;134233118&quot;:true,&quot;335559738&quot;:450,&quot;335572079&quot;:12,&quot;335572080&quot;:6,&quot;335572081&quot;:10921365,&quot;469789806&quot;:&quot;single&quot;}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">mssql-python<\/span><span data-contrast=\"none\">\u00a0now detects which style\u00a0you&#8217;re\u00a0using based on the parameter type:<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/p>\n<ul>\n<li aria-setsize=\"-1\" data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"4\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><b><span data-contrast=\"none\">tuple\/list<\/span><\/b><span data-contrast=\"none\">\u00a0\u2192\u00a0<\/span><span data-contrast=\"none\">qmark<\/span><span data-contrast=\"none\">\u00a0(<\/span><span data-contrast=\"none\">?<\/span><span data-contrast=\"none\">)<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<\/ul>\n<ul>\n<li aria-setsize=\"-1\" data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"4\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"2\" data-aria-level=\"1\"><b><span data-contrast=\"none\">dict<\/span><\/b><span data-contrast=\"none\">\u00a0\u2192\u00a0<\/span><span data-contrast=\"none\">pyformat<\/span><span data-contrast=\"none\">\u00a0(<\/span><span data-contrast=\"none\">%(name)s<\/span><span data-contrast=\"none\">)<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<\/ul>\n<p><span data-contrast=\"none\">No configuration needed. Existing\u00a0<\/span><span data-contrast=\"none\">qmark<\/span><span data-contrast=\"none\">\u00a0code requires\u00a0<\/span><b><span data-contrast=\"none\">zero changes<\/span><\/b><span data-contrast=\"none\">.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/p>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">from mssql_python import connect\u00a0\r\n\u00a0\r\n# qmark - works exactly as before\u00a0\r\ncursor.execute(\"SELECT * FROM users WHERE id = ?\", (42,))\u00a0\r\n\u00a0\r\n# pyformat - just pass a dict!\u00a0\r\ncursor.execute(\"SELECT * FROM users WHERE id = %(id)s\", {\"id\": 42})<\/code><\/pre>\n<p aria-level=\"2\"><span style=\"font-size: 18pt;\"><b>How It Works<\/b>\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">When you pass a\u00a0dict\u00a0to\u00a0<\/span><span data-contrast=\"none\">execute()<\/span><span data-contrast=\"none\">, the driver:<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/p>\n<ol>\n<li aria-setsize=\"-1\" data-leveltext=\"%1.\" data-font=\"Calibri,Times New Roman\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><b><span data-contrast=\"none\">Scans<\/span><\/b><span data-contrast=\"none\">\u00a0the SQL for\u00a0<\/span><span data-contrast=\"none\">%(name)s<\/span><span data-contrast=\"none\">\u00a0placeholders (context-aware\u00a0&#8211;\u00a0skips string literals, comments, and bracketed identifiers).<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<li aria-setsize=\"-1\" data-leveltext=\"%1.\" data-font=\"Calibri,Times New Roman\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><b><span data-contrast=\"none\">Validates<\/span><\/b><span data-contrast=\"none\">\u00a0that every placeholder has a matching key in the dict.<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<li aria-setsize=\"-1\" data-leveltext=\"%1.\" data-font=\"Calibri,Times New Roman\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><b><span data-contrast=\"none\">Builds<\/span><\/b><span data-contrast=\"none\">\u00a0a positional tuple in placeholder order (duplicating values for reused parameters).<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<li aria-setsize=\"-1\" data-leveltext=\"%1.\" data-font=\"Calibri,Times New Roman\" data-listid=\"1\" data-list-defn-props=\"{&quot;335552541&quot;:0,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><b><span data-contrast=\"none\">Replaces<\/span><\/b><span data-contrast=\"none\">\u00a0each\u00a0<\/span><span data-contrast=\"none\">%(name)s<\/span><span data-contrast=\"none\">\u00a0with\u00a0<\/span><span data-contrast=\"none\">?<\/span><span data-contrast=\"none\">\u00a0and sends the rewritten query to ODBC.<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<\/ol>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">User Code\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ODBC Layer\u00a0\r\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u00a0\r\ncursor.execute(\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0SQLBindParameter(1, \"active\")\u00a0\r\n\u00a0 \"WHERE status = %(status)s\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0SQLBindParameter(2, \"USA\")\u00a0\r\n\u00a0\u00a0 AND country = %(country)s\",\u00a0\u00a0\u00a0\u00a0\u00a0 \u2192\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0SQLExecute(\u00a0\r\n\u00a0 {\"status\": \"active\",\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"WHERE status = ?\u00a0\r\n\u00a0\u00a0 \"country\": \"USA\"}\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 AND country = ?\"\u00a0\r\n)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ) <\/code><\/pre>\n<p><span data-contrast=\"none\">The ODBC layer always works with\u00a0positional\u00a0<\/span><span data-contrast=\"none\">?<\/span><span data-contrast=\"none\">\u00a0placeholders. The\u00a0pyformat\u00a0conversion is purely a developer-facing convenience with zero overhead to database communication.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/p>\n<p aria-level=\"2\"><span style=\"font-size: 18pt;\"><b>Clear Error Messages<\/b>\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">Mismatched styles or missing parameters produce actionable errors\u00a0&#8211;\u00a0not cryptic database exceptions:<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/p>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">cursor.execute(\"WHERE id = %(id)s AND name = %(name)s\", {\"id\": 42})\u00a0\r\n# KeyError: Missing required parameter(s): 'name'.\u00a0\r\n\u00a0\r\ncursor.execute(\"WHERE id = ?\", {\"id\": 42})\u00a0\r\n# TypeError: query uses positional placeholders (?), but dict was provided.\u00a0\r\n\u00a0\r\ncursor.execute(\"WHERE id = %(id)s\", (42,))\u00a0\r\n# TypeError: query uses named placeholders (%(name)s), but tuple was provided.<\/code><\/pre>\n<p aria-level=\"2\"><span style=\"font-size: 18pt;\"><b>Real-World Examples<\/b>\u00a0<\/span><\/p>\n<p aria-level=\"3\"><b><span data-contrast=\"none\"><span style=\"font-size: 14pt;\"><strong>Example 1: <\/strong><\/span>Web Application<\/span><\/b><\/p>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">def add_user(name, email):\u00a0\r\n\u00a0\u00a0\u00a0 with connect(connection_string) as conn:\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 with conn.cursor() as cursor:\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0cursor.execute(\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"INSERT INTO users (name, email) VALUES (%(name)s, %(email)s)\",\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\"name\": name, \"email\": email}\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ) <\/code><\/pre>\n<p aria-level=\"3\"><b><span data-contrast=\"none\"><span style=\"font-size: 14pt;\"><strong>Example 2: <\/strong><\/span>Batch Operations<\/span><\/b><span data-ccp-props=\"{&quot;134233118&quot;:true,&quot;335559738&quot;:375}\">\u00a0<\/span><\/p>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">cursor.executemany(\u00a0\r\n\u00a0\u00a0\u00a0 \"INSERT INTO users (name, age) VALUES (%(name)s, %(age)s)\",\u00a0\r\n\u00a0\u00a0\u00a0 [{\"name\": \"Alice\", \"age\": 30}, {\"name\": \"Bob\", \"age\": 25}]\u00a0\r\n) <\/code><\/pre>\n<p aria-level=\"3\"><b><span data-contrast=\"none\"><span style=\"font-size: 14pt;\"><strong>Example 3: <\/strong><\/span>Financial Transactions<\/span><\/b><span data-ccp-props=\"{&quot;134233118&quot;:true,&quot;335559738&quot;:375}\">\u00a0<\/span><\/p>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">def transfer_funds(from_acct, to_acct, amount):\u00a0\r\n\u00a0\u00a0\u00a0 with connect(connection_string) as conn:\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 with conn.cursor() as cursor:\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0cursor.execute(\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"UPDATE accounts SET balance = balance - %(amount)s WHERE id = %(id)s\",\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\"amount\": amount, \"id\": from_acct}\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0cursor.execute(\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"UPDATE accounts SET balance = balance + %(amount)s WHERE id = %(id)s\",\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\"amount\": amount, \"id\": to_acct}\u00a0\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 )\u00a0\r\n\u00a0\u00a0\u00a0 # Automatic commit on success, rollback on failure <\/code><\/pre>\n<p aria-level=\"2\"><span style=\"font-size: 18pt;\"><b>Things to Keep in Mind<\/b>\u00a0<\/span><\/p>\n<ul>\n<li aria-setsize=\"-1\" data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"2\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"1\" data-aria-level=\"1\"><b><span data-contrast=\"none\">Don&#8217;t\u00a0mix styles in one query<\/span><\/b><span data-contrast=\"none\">. Use\u00a0either ?\u00a0or %(name)s, not both. The driver\u00a0determines\u00a0which style\u00a0you&#8217;re\u00a0using from the parameter type (tuple vs\u00a0dict), not from the SQL text. If placeholders\u00a0don&#8217;t\u00a0match the parameter type,\u00a0you&#8217;ll\u00a0get a clear\u00a0TypeError\u00a0explaining the mismatch. If both placeholder types appear in the SQL, only one set gets substituted,\u00a0leading to parameter count mismatches at execution time.<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<\/ul>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\"># Mixing styles - raises TypeError\u00a0\r\ncursor.execute( \"SELECT * FROM users WHERE id = ? AND name = %(name)s\", {\"name\": \"Alice\"} # Driver finds %(name)s but also sees unmatched ? )\u00a0\r\n# ODBC error: parameter count mismatch (2 placeholders, 1 value)\u00a0\r\n# Pick one style and use it consistently\u00a0\r\ncursor.execute( \"SELECT * FROM users WHERE id = %(id)s AND name = %(name)s\", {\"id\": 42, \"name\": \"Alice\"} ) <\/code><\/pre>\n<ul>\n<li aria-setsize=\"-1\" data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"2\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"2\" data-aria-level=\"1\"><b><span data-contrast=\"none\">Extra\u00a0dict\u00a0keys are OK.<\/span><\/b><span data-contrast=\"none\">\u00a0\u00a0Unused parameters are silently\u00a0ignored,\u00a0this is by design to enable parameter dictionary reuse across different queries.<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<\/ul>\n<ul>\n<li aria-setsize=\"-1\" data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"2\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"3\" data-aria-level=\"1\"><b><span data-contrast=\"none\">SQL injection safe.<\/span><\/b><span data-contrast=\"none\">\u00a0Both styles use ODBC parameter binding under the hood. Values are never interpolated into the SQL\u00a0string,\u00a0they are always safely bound by the driver.<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<\/ul>\n<ul>\n<li aria-setsize=\"-1\" data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"2\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"4\" data-aria-level=\"1\"><b><span data-contrast=\"none\">Literal\u00a0<\/span><\/b><b><span data-contrast=\"none\">%<\/span><\/b><b><span data-contrast=\"none\">\u00a0in SQL.<\/span><\/b><span data-contrast=\"none\">\u00a0Use\u00a0<\/span><span data-contrast=\"none\">%%<\/span><span data-contrast=\"none\">\u00a0to\u00a0escape\u00a0if you need a literal\u00a0<\/span><span data-contrast=\"none\">%(&#8230;)s<\/span><span data-contrast=\"none\">\u00a0pattern in your query text.<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<\/ul>\n<pre class=\"prettyprint language-py\"><code class=\"language-py\">cursor.execute(\u00a0\r\n\u00a0\u00a0\u00a0 \"SELECT * FROM users WHERE name LIKE %(pattern)s\",\u00a0\r\n\u00a0\u00a0\u00a0 {\"pattern\": \"%alice%\"}\u00a0 # The % inside the VALUE is fine\u00a0\r\n)\u00a0\r\n\u00a0\r\n# But if you need a literal %(...)s in SQL text itself, use %%\u00a0\r\ncursor.execute(\u00a0\r\n\u00a0\u00a0\u00a0 \"SELECT '%%(example)s' AS literal WHERE id = %(id)s\",\u00a0\r\n\u00a0\u00a0\u00a0 {\"id\": 42}\u00a0\r\n)\u00a0<\/code><span data-ccp-props=\"{&quot;335559685&quot;:720,&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/pre>\n<ul>\n<li aria-setsize=\"-1\" data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"2\" data-list-defn-props=\"{&quot;335552541&quot;:1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;\uf0b7&quot;,&quot;469777815&quot;:&quot;multilevel&quot;}\" data-aria-posinset=\"5\" data-aria-level=\"1\"><b><span data-contrast=\"none\">mssql_python.paramstyle\u00a0reports &#8220;pyformat&#8221;<\/span><\/b><span data-contrast=\"none\">. The DB-API 2.0 spec only allows a single value for this module-level constant. We set it to\u00a0pyformat\u00a0because\u00a0it&#8217;s\u00a0the more expressive style and the one we recommend for new code. But\u00a0qmark\u00a0is fully supported at runtime, the driver accepts both styles transparently based on whether you pass a tuple or a dict. Think of\u00a0paramstyle\u00a0= &#8220;pyformat&#8221; as the advertised default, not a limitation.<\/span><span data-ccp-props=\"{&quot;335559738&quot;:75,&quot;335559739&quot;:75}\">\u00a0<\/span><\/li>\n<\/ul>\n<p aria-level=\"2\"><span style=\"font-size: 18pt;\"><b>Compatibility\u00a0at a Glance<\/b>\u00a0<\/span><\/p>\n<table data-tablestyle=\"MsoNormalTable\" data-tablelook=\"1184\" aria-rowcount=\"8\">\n<tbody>\n<tr aria-rowindex=\"1\">\n<td data-celllook=\"65536\"><b><span data-contrast=\"none\">Feature<\/span><\/b><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"65536\"><b><span data-contrast=\"none\">qmark\u00a0(?)<\/span><\/b><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"65536\"><b><span data-contrast=\"none\">pyformat\u00a0(%<\/span><\/b><strong>(name)s<\/strong><b><span data-contrast=\"none\">)<\/span><\/b><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<\/tr>\n<tr aria-rowindex=\"2\">\n<td data-celllook=\"4369\"><span data-contrast=\"none\">cursor.execute()<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<\/tr>\n<tr aria-rowindex=\"3\">\n<td data-celllook=\"4369\"><span data-contrast=\"none\">cursor.executemany()<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<\/tr>\n<tr aria-rowindex=\"4\">\n<td data-celllook=\"4369\"><span data-contrast=\"none\">connection.execute()<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<\/tr>\n<tr aria-rowindex=\"5\">\n<td data-celllook=\"4369\"><span data-contrast=\"none\">Parameter reuse<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u274c<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<\/tr>\n<tr aria-rowindex=\"6\">\n<td data-celllook=\"4369\"><span data-contrast=\"none\">Stored procedures<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<\/tr>\n<tr aria-rowindex=\"7\">\n<td data-celllook=\"4369\"><span data-contrast=\"none\">All SQL data types<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<\/tr>\n<tr aria-rowindex=\"8\">\n<td data-celllook=\"4369\"><span data-contrast=\"none\">Backward compatible\u00a0with qmark paramstyle<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">\u2705<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<td data-celllook=\"4369\"><span data-contrast=\"none\">N\/A (new)<\/span><span data-ccp-props=\"{&quot;335559738&quot;:300,&quot;335559739&quot;:300}\">\u00a0<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p aria-level=\"2\"><span style=\"font-size: 18pt;\"><b>Takeaway<\/b>\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">Use\u00a0<\/span><span data-contrast=\"none\">?<\/span><span data-contrast=\"none\">\u00a0for quick, simple queries. Use\u00a0<\/span><span data-contrast=\"none\">%(name)s<\/span><span data-contrast=\"none\">\u00a0for complex, multi-parameter queries where clarity and reuse matter. You\u00a0don&#8217;t\u00a0have to pick a side\u00a0&#8211;\u00a0use whichever fits the situation. The driver handles the rest.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">Whether\u00a0you&#8217;re\u00a0building dynamic queries, or simply want more readable SQL, dual\u00a0paramstyle\u00a0support makes\u00a0<\/span><span data-contrast=\"none\">mssql-python<\/span><span data-contrast=\"none\">\u00a0work the way you already think.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true}\">\u00a0<\/span><\/p>\n<p><strong><span style=\"font-size: 18pt;\">Try It and Share Your Feedback!\u00a0<\/span><\/strong><\/p>\n<p>We invite you to:<\/p>\n<ol>\n<li>Check-out the <a id=\"menurrmp\" class=\"fui-Link ___1q1shib f2hkw1w f3rmtva f1ewtqcl fyind8e f1k6fduh f1w7gpdv fk6fouc fjoy568 figsok6 f1s184ao f1mk8lai fnbmjn9 f1o700av f13mvf36 f1cmlufx f9n3di6 f1ids18y f1tx3yz7 f1deo86v f1eh06m1 f1iescvh fhgqx19 f1olyrje f1p93eir f1nev41a f1h8hb77 f1lqvz6u f10aw75t fsle3fq f17ae5zn\" title=\"https:\/\/github.com\/microsoft\/mssql-python\" href=\"https:\/\/github.com\/microsoft\/mssql-python\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Link mssql-python\u00a0\">mssql-python\u00a0<\/a>driver and integrate it into your projects.<\/li>\n<li>Share your thoughts:\u00a0Open <a id=\"menurrmr\" class=\"fui-Link ___1q1shib f2hkw1w f3rmtva f1ewtqcl fyind8e f1k6fduh f1w7gpdv fk6fouc fjoy568 figsok6 f1s184ao f1mk8lai fnbmjn9 f1o700av f13mvf36 f1cmlufx f9n3di6 f1ids18y f1tx3yz7 f1deo86v f1eh06m1 f1iescvh fhgqx19 f1olyrje f1p93eir f1nev41a f1h8hb77 f1lqvz6u f10aw75t fsle3fq f17ae5zn\" title=\"https:\/\/github.com\/microsoft\/mssql-python\/issues\" href=\"https:\/\/github.com\/microsoft\/mssql-python\/issues\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Link issues\">issues<\/a>, suggest features, and contribute to the project.<\/li>\n<li>Join the conversation:\u00a0<a id=\"menurrmt\" class=\"fui-Link ___1q1shib f2hkw1w f3rmtva f1ewtqcl fyind8e f1k6fduh f1w7gpdv fk6fouc fjoy568 figsok6 f1s184ao f1mk8lai fnbmjn9 f1o700av f13mvf36 f1cmlufx f9n3di6 f1ids18y f1tx3yz7 f1deo86v f1eh06m1 f1iescvh fhgqx19 f1olyrje f1p93eir f1nev41a f1h8hb77 f1lqvz6u f10aw75t fsle3fq f17ae5zn\" title=\"https:\/\/github.com\/microsoft\/mssql-python\/discussions\" href=\"https:\/\/github.com\/microsoft\/mssql-python\/discussions\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Link GitHub Discussions\">GitHub Discussions<\/a>\u00a0|\u00a0<a id=\"menurrmv\" class=\"fui-Link ___1q1shib f2hkw1w f3rmtva f1ewtqcl fyind8e f1k6fduh f1w7gpdv fk6fouc fjoy568 figsok6 f1s184ao f1mk8lai fnbmjn9 f1o700av f13mvf36 f1cmlufx f9n3di6 f1ids18y f1tx3yz7 f1deo86v f1eh06m1 f1iescvh fhgqx19 f1olyrje f1p93eir f1nev41a f1h8hb77 f1lqvz6u f10aw75t fsle3fq f17ae5zn\" title=\"https:\/\/techcommunity.microsoft.com\/category\/sql-server\/blog\/sqlserver\" href=\"https:\/\/techcommunity.microsoft.com\/category\/sql-server\/blog\/sqlserver\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Link SQL Server Tech Community\">SQL Server Tech Community<\/a>.<\/li>\n<\/ol>\n<p><div class=\"alert alert-success\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Lightbulb\"><\/i><strong>Use Python Driver with Free Azure SQL Database<\/strong><\/p>You can use the Python Driver with the free version of Azure SQL Database!<\/p>\n<p>\u2705 <a id=\"menurrn1\" class=\"fui-Link ___1q1shib f2hkw1w f3rmtva f1ewtqcl fyind8e f1k6fduh f1w7gpdv fk6fouc fjoy568 figsok6 f1s184ao f1mk8lai fnbmjn9 f1o700av f13mvf36 f1cmlufx f9n3di6 f1ids18y f1tx3yz7 f1deo86v f1eh06m1 f1iescvh fhgqx19 f1olyrje f1p93eir f1nev41a f1h8hb77 f1lqvz6u f10aw75t fsle3fq f17ae5zn\" title=\"https:\/\/learn.microsoft.com\/en-us\/azure\/azure-sql\/database\/free-offer?view=azuresql\" href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/azure-sql\/database\/free-offer?view=azuresql\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Link Deploy Azure SQL Database for free\">Deploy Azure SQL Database for free<\/a><\/p>\n<p>\u2705 <a id=\"menurrn3\" class=\"fui-Link ___1q1shib f2hkw1w f3rmtva f1ewtqcl fyind8e f1k6fduh f1w7gpdv fk6fouc fjoy568 figsok6 f1s184ao f1mk8lai fnbmjn9 f1o700av f13mvf36 f1cmlufx f9n3di6 f1ids18y f1tx3yz7 f1deo86v f1eh06m1 f1iescvh fhgqx19 f1olyrje f1p93eir f1nev41a f1h8hb77 f1lqvz6u f10aw75t fsle3fq f17ae5zn\" title=\"https:\/\/learn.microsoft.com\/en-us\/azure\/azure-sql\/managed-instance\/free-offer?view=azuresql\" href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/azure-sql\/managed-instance\/free-offer?view=azuresql\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Link Deploy Azure SQL Managed Instance for free\">Deploy Azure SQL Managed Instance for free<\/a> Perfect for testing, development, or learning scenarios without incurring costs.<\/div><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Reviewed by: Sumit Sarabhai If\u00a0you&#8217;ve\u00a0been writing SQL in Python, you already know the debate: positional parameters (?) or named parameters (%(name)s)? Some developers swear by the conciseness of positional. Others prefer the clarity of named. With mssql-python, you no longer need to choose\u00a0&#8211;\u00a0we support\u00a0both.\u00a0\u00a0We&#8217;ve added dual parameter style support to\u00a0mssql-python, enabling both\u00a0qmark\u00a0and\u00a0pyformat\u00a0parameter styles in Python [&hellip;]<\/p>\n","protected":false},"author":194549,"featured_media":10119,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3,1],"tags":[1277,1281,1283,1282,1278,1280],"class_list":["post-10280","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure","category-python","tag-azure-sql","tag-client-driver","tag-python-driver-for-azure-sql","tag-python-driver-for-sql-server","tag-sql-server","tag-sql-server-2025"],"acf":[],"blog_post_summary":"<p>Reviewed by: Sumit Sarabhai If\u00a0you&#8217;ve\u00a0been writing SQL in Python, you already know the debate: positional parameters (?) or named parameters (%(name)s)? Some developers swear by the conciseness of positional. Others prefer the clarity of named. With mssql-python, you no longer need to choose\u00a0&#8211;\u00a0we support\u00a0both.\u00a0\u00a0We&#8217;ve added dual parameter style support to\u00a0mssql-python, enabling both\u00a0qmark\u00a0and\u00a0pyformat\u00a0parameter styles in Python [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/posts\/10280","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/users\/194549"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/comments?post=10280"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/posts\/10280\/revisions"}],"predecessor-version":[{"id":10289,"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/posts\/10280\/revisions\/10289"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/media\/10119"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/media?parent=10280"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/categories?post=10280"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/python\/wp-json\/wp\/v2\/tags?post=10280"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}