Python · SQL Server · Complete CRUD Series

Python SQL Server CRUD Tutorial with pyodbc: 10 Examples

A file-by-file analysis of a complete SQL Server learning sequence: verify the ODBC driver, create SQF_DB, build dbo.SCRIPT, insert one or many records, read, update, delete, truncate safely, diagnose the connection and run the complete CRUD workflow.

10 Python scripts SQL Server CRUD Parameterized SQL Code review + improvements

Lab Overview

Lab 6 of 8Estimated time: 90 minutesDifficulty: Intermediate

Prerequisites / What You’ll Need

  • Python 3.x and pyodbc
  • Disposable SQL Server practice database
  • CREATE, SELECT, INSERT, UPDATE and DELETE permissions
Quick answer

The ten scripts are valid training examples for pyodbc and SQL Server. They progressively create the database/table and demonstrate parameterized CRUD operations. Their strongest features are driver verification, create-if-missing DDL, bound values, identity retrieval with OUTPUT INSERTED, guarded truncation and connection diagnostics. For production use, centralize configuration, remove repeated setup, define transaction ownership, add structured error handling, protect credentials/certificates and make demo inserts idempotent.

  • Run files 01–09 individually for focused learning; file 10 repeats the complete workflow.
  • SQL values are parameterized, while the trusted database identifier is interpolated only for CREATE DATABASE.
  • The current full demo commits each helper independently, so it is a sequence of transactions—not one all-or-nothing transaction.

Python SQL Server CRUD Operations Covered in This Tutorial

This pillar tutorial brings the full Python SQL Server CRUD search intent into one learning path: connect with pyodbc, create database objects, insert one or many rows, select data, update by key, delete safely, understand TRUNCATE, test the connection, and control transactions.

Related Search Topics

Python CRUD operations SQL Server · pyodbc insert select update delete · SQL Server Python transaction example · Python pyodbc complete tutorial

Python SQL Server CRUD Learning Path

Setup01 database
02 table
Create03 one row
04 many rows
Read/Update05 select
06 update
Delete07 one row
08 all rows
Verify/Combine09 diagnostics
10 full CRUD
FilePrimary lessonChanges data?Safe rerun behavior
01_create_database.pyCreate database if missingDDLExisting DB retained
02_create_table.pyCreate table if missingDDLExisting table retained
03_insert_script.pySingle insert + returned identityYesAdds another row
04_insert_many.pyBatch insertYesAdds three more rows
05_select_scripts.pyRead ordered rowsSetup may; SELECT does notDoes not alter rows
06_update_script.pyUpdate by primary keyYesUpdates first row or creates sample
07_delete_script.pyCreate/delete disposable rowYesNet row count normally unchanged
08_truncate_scripts.pyGuarded full-table removalOnly when enabledDefault skips
09_test_connection.pyConnection/server identityMay create DB firstDatabase check repeats
10_full_crud_demo.pyCombined workflowYesAdds demo rows on every run
Use a training database: files 03, 04, 06, 07 and 10 modify data; file 08 can remove every row. Confirm SERVER, DB_NAME and RUN_TRUNCATE before every run.

2. Shared Driver and Connection Pattern

SERVER = r"localhost\WINCC"
DB_NAME = "SQF_DB"
DRIVER_NAME = "ODBC Driver 17 for SQL Server"

if DRIVER_NAME not in pyodbc.drivers():
    raise RuntimeError(
        f"{DRIVER_NAME} is not installed. Installed drivers: {pyodbc.drivers()}"
    )

Each file verifies the driver before connecting. The master connection creates the database; the database connection handles table/CRUD work. Windows authentication uses the identity running Python.

MASTER_CONN = (
    f"DRIVER={DRIVER};SERVER={SERVER};DATABASE=master;"
    "Trusted_Connection=yes;TrustServerCertificate=yes;Connection Timeout=5;"
)
DB_CONN = (
    f"DRIVER={DRIVER};SERVER={SERVER};DATABASE={DB_NAME};"
    "Trusted_Connection=yes;TrustServerCertificate=yes;Connection Timeout=5;"
)

TrustServerCertificate=yes is useful in a controlled lab with an untrusted/self-signed certificate. Production should deploy a trusted SQL Server certificate. The code is also repeated in every file; a shared db_config.py would prevent configuration drift.

3. File 01—Create the Database

with pyodbc.connect(MASTER_CONN, autocommit=True) as conn:
    exists = conn.cursor().execute("SELECT DB_ID(?)", DB_NAME).fetchone()[0]
    if exists is None:
        conn.cursor().execute(f"CREATE DATABASE [{DB_NAME}]")

Connecting to master is necessary because SQF_DB might not exist. CREATE DATABASE requires execution outside a user transaction, so the master connection uses autocommit. DB_ID(?) safely binds the database name as a value for the check.

The DDL identifier cannot be supplied through a normal ? value parameter, so it is interpolated. This is acceptable only because DB_NAME is a trusted constant. If configurable, validate it against a strict identifier allowlist and quote it correctly.

4. File 02—Create dbo.SCRIPT

CREATE TABLE dbo.SCRIPT
(
    ScriptID INT IDENTITY(1,1) PRIMARY KEY,
    ScriptName NVARCHAR(100) NOT NULL,
    ScriptText NVARCHAR(MAX) NOT NULL,
    IsActive BIT NOT NULL DEFAULT(1),
    CreatedAt DATETIME2(0) NOT NULL DEFAULT SYSDATETIME()
);

The schema demonstrates identity keys, Unicode text, Boolean status and a server-generated timestamp. OBJECT_ID(..., N'U') prevents duplicate creation. It does not validate or upgrade an existing table with incorrect columns/types; production schema changes require migrations.

Insert Data into SQL Server with Python pyodbc

Single insert with generated identity

INSERT INTO dbo.SCRIPT (ScriptName, ScriptText, IsActive)
OUTPUT INSERTED.ScriptID
VALUES (?, ?, ?)

OUTPUT INSERTED.ScriptID reliably returns the identity created by the statement. Values are bound separately, so apostrophes inside ScriptText do not break SQL and untrusted input is not interpreted as SQL syntax.

Batch insert

rows = [
    ("Temperature Check", "SELECT 25.4 AS TemperatureC", True),
    ("Alarm Reset", "PRINT 'Alarm reset'", False),
    ("Production Check", "SELECT GETDATE() AS CheckedAt", True),
]
cur.fast_executemany = True
cur.executemany(sql, rows)
conn.commit()

executemany() applies the same parameterized statement to each tuple. fast_executemany can reduce round trips with the Microsoft driver. The returned count is len(rows), representing rows submitted after success—not a server-returned affected-row count.

Both demonstrations add duplicate logical examples when rerun. Add a unique business key, MERGE/upsert policy, test-batch cleanup or explicit idempotency rule when repetition must not duplicate data.

6. File 05—Read and Display Records

SELECT ScriptID, ScriptName, ScriptText, IsActive, CreatedAt
FROM dbo.SCRIPT
ORDER BY ScriptID

The SELECT returns all rows in identity order, prints each pyodbc Row object and handles an empty result. For real applications, select only required columns, add filters/paging, map rows into dictionaries or domain objects, and avoid printing sensitive script text to uncontrolled logs.

7. File 06—Update by Primary Key

UPDATE dbo.SCRIPT
SET ScriptText = ?, IsActive = ?
WHERE ScriptID = ?

get_or_create_sample() ensures a row exists, then the update targets one primary-key value and returns cursor.rowcount. Confirm the affected count before reporting success. In a multi-user system, consider optimistic concurrency—such as comparing a rowversion—so one user does not silently overwrite another user’s change.

The selected sample is simply the lowest ScriptID; this is deterministic but may modify a row unrelated to the update lesson if the table contains real data. A training-specific row name or batch ID is safer.

8. File 07—Delete a Disposable Row

new_id = cursor.execute(
    "INSERT ... OUTPUT INSERTED.ScriptID VALUES (?, ?, ?)", ...
).fetchone()[0]

cursor.execute("DELETE FROM dbo.SCRIPT WHERE ScriptID = ?", new_id)

The file creates a row specifically for deletion, captures its identity, then deletes exactly that row. This is safer than choosing an arbitrary existing row. The insert and delete are committed separately; if deletion fails, the disposable row remains. For zero net change, perform both operations in one transaction and roll back the demonstration intentionally.

9. File 08—Guarded TRUNCATE TABLE

RUN_TRUNCATE = False

def truncate_scripts(conn, confirm=False):
    if not confirm:
        raise ValueError("TRUNCATE cancelled because confirm=False.")
    conn.cursor().execute("TRUNCATE TABLE dbo.SCRIPT")
    conn.commit()

Two gates protect the operation: the global flag defaults to false and the function requires confirm=True. TRUNCATE removes all rows, normally resets the identity seed, uses schema-level locking and requires stronger permission than ordinary DELETE. It can be blocked by certain foreign-key relationships and other dependencies.

Destructive operation: never enable RUN_TRUNCATE in a production/shared database without an approved backup, exact target verification, maintenance authorization and recovery plan.

10. File 09—Test the Connection

SELECT
    @@SERVERNAME AS ServerName,
    DB_NAME() AS DatabaseName,
    SYSTEM_USER AS LoginName,
    @@VERSION AS VersionInfo

This is an excellent diagnostic query: it proves the actual server, database, login and SQL Server version. However, the script calls create_database() before testing, so it is not strictly read-only. A pure connection-test utility should connect to an expected existing database and fail without creating anything.

@@SERVERNAME may not match the connection string exactly after server renaming or alias use; treat the output as diagnostic evidence rather than string-equality validation alone.

11. File 10—Complete CRUD Demonstration

The final file combines setup, single insert, batch insert, SELECT, update, delete, optional truncate and final SELECT.

welcome_id = insert_script(conn, "Full CRUD Welcome", ...)
insert_many(conn, [
    ("Full CRUD Temperature", ...),
    ("Full CRUD Alarm", ...),
])
rows = select_scripts(conn)
alarm_id = next(
    row.ScriptID for row in reversed(rows)
    if row.ScriptName == "Full CRUD Alarm"
)
update_script(conn, welcome_id, "PRINT 'System ready'", True)
delete_script(conn, alarm_id)

Finding the newest matching alarm by reversing the ScriptID-ordered rows works for this single-process demo. In concurrent use, another identical row could interfere. Capture inserted identities directly—such as using OUTPUT INSERTED.ScriptID for each required row—or add an unambiguous batch ID.

The next(...) expression raises StopIteration if no matching row exists. Although the script inserts that row first, explicit handling would produce a clearer diagnostic.

pyodbc Transactions, Commit and Rollback Explained

The full CRUD demo wraps operations in try/except pyodbc.Error and calls conn.rollback() on failure. However, every helper function calls conn.commit() internally. Therefore:

  • The database/table setup is committed.
  • The single insert is committed.
  • The batch insert is committed.
  • The update and delete are committed separately.
  • A failure later can roll back only the current uncommitted work—not earlier commits.
Conclusion: the workflow is not atomic. This may be intentional for independent teaching steps. If the business requirement is all-or-nothing CRUD, remove commits from the helpers, let the caller own the transaction, commit once at the end, and roll back once on any failure.
with pyodbc.connect(DB_CONN) as conn:
    try:
        create_table_without_commit(conn)
        new_id = insert_without_commit(conn, ...)
        update_without_commit(conn, new_id, ...)
        conn.commit()             # one transaction boundary
    except Exception:
        conn.rollback()
        raise

Database creation remains separate because it uses the autocommit master connection.

13. Refactoring and Production Design

All ten files repeat driver checks, connection strings, database creation and often table creation. Repetition is helpful when each lesson must run alone, but maintainable applications should separate responsibilities:

sql_server_demo/
├── config.py          # validated settings
├── connection.py      # connection factories
├── schema.py          # database/table migrations
├── repository.py      # insert/select/update/delete
├── diagnostics.py     # read-only connection checks
├── cli.py             # commands and destructive confirmations
└── tests/             # isolated integration tests

Recommended changes

  • Load environment-specific configuration from a controlled source.
  • Use trusted certificates instead of globally trusting the server certificate.
  • Use separate deployment and runtime accounts with least privilege.
  • Let the top-level workflow define transaction boundaries.
  • Add unique keys or batch IDs to make retries idempotent.
  • Use logging with operation name, row count, duration and exception details.
  • Catch expected pyodbc.Error cases and re-raise after rollback.
  • Add integration tests against a disposable database.
  • Remove unused MASTER_CONN/RUN_TRUNCATE from files that do not need them—or explain that uniform configuration is intentional.
  • Use migration scripts to compare/upgrade existing schemas.

14. Troubleshooting Checklist

SymptomLikely causeCorrection
ODBC Driver 17 not installedDriver missing or different version/nameCheck pyodbc.drivers() and install approved driver
Server not foundNamed instance, SQL Browser, firewall or networkVerify localhost\WINCC and SQL services
Login failedWindows identity lacks accessCheck SYSTEM_USER and grants
CREATE DATABASE deniedInsufficient server permissionUse DBA provisioning or approved deployment identity
String/binary truncatedScriptName longer than NVARCHAR(100)Validate input length or migrate schema intentionally
Update/delete affects zero rowsID does not existCheck rowcount and current data
Duplicate demo rowsInsert files/full demo rerunUse unique key, batch ID or approved cleanup
TRUNCATE failsPermission, FK/reference or lockInspect dependencies and never bypass safety controls
Rollback does not remove earlier changesHelpers already committedMove commit control to top-level transaction owner

Hands-On Lab: Run and Verify All 10 Scripts

Practical sequence
Before you start
  • Use an authorized disposable/training SQL Server instance.
  • Keep RUN_TRUNCATE = False until the dedicated final test.
  • Record server, database, Windows identity and installed ODBC driver.
  • Estimated time: 45 minutes.
1

Provision and inspect

Run files 01 and 02, then inspect SQF_DB and dbo.SCRIPT in SQL Server Management Studio.

The database/table exist once with the expected key, types, defaults and nullability.
2

Create and read records

Run files 03, 04 and 05. Reconcile printed identities and row count with SQL.

Four new rows exist and values preserve apostrophes/Unicode through parameters.
3

Update, delete and diagnose

Run files 06, 07 and 09. Verify affected counts and diagnostic identity.

One known row updates, the disposable row is removed, and server/database/login match expectations.
4

Run full CRUD and test truncate separately

Run file 10 with truncation disabled. Only after backup and target verification, test file 08 in the disposable database.

CRUD results are understood; guarded truncation removes all rows and resets identity as expected.

Related Python and SQL Server Tutorials

Continue through the Softwell Python–SQL Server learning path:

Frequently asked questions

What does CRUD mean?

CRUD means Create, Read, Update and Delete—the core persistent-data operations demonstrated by the scripts.

What is the difference between DELETE and TRUNCATE TABLE?

DELETE can target selected rows. TRUNCATE removes every row, normally resets identity and has different permission, locking and foreign-key restrictions.

Is the full CRUD demo one atomic transaction?

No. Its helper functions commit individually, so a later rollback cannot undo earlier committed operations. Move commits to the caller for an all-or-nothing transaction.

Reviewed by Bhawesh Kumar SinghIndustrial Automation Trainer and Industry 4.0 Consultant · Softwell Automation · 21+ years industry experience

Get the Python + SQL Server CRUD syllabus

Share your details—a Softwell advisor will contact you with batch dates, fees and project-practice options.

No spam. Used only to share course details for this enquiry.

Master Python and SQL Server CRUD with practical scripts

Join live online, Pune classroom or corporate Industry 4.0 training.

Request Course Details
Verified learning pathway

Discuss Python SQL Server CRUD Training

Explore practical curriculum, software, hardware and batch options for this technology.

Content reviewed: 2 August 2026

☎ Call WhatsApp ✉ Email Enquire Now