SQL Server · DML · SCADA Data Lab

SQL DML Scripting for Automation

Learn to query and safely change industrial data with SELECT, INSERT, UPDATE and DELETE—using SQF_DB.dbo.tblEvent as a practical PLC/SCADA example.

SELECT and filter INSERT and OUTPUT Safe UPDATE Controlled DELETE

Learning Overview

Level: SQL fundamentalsFormat: Blog + practical labExample: SQF_DB.dbo.tblEventEstimated time: 180 minutes

Prerequisites / What You’ll Need

  • SQL Server or SQL Server Express test instance
  • SQL Server Management Studio (SSMS) or Azure Data Studio
  • The practice dbo.tblEvent table from the companion DDL guide
  • Permission to read and modify data in a disposable database
  • A current backup before testing on any existing database
Core idea

DML changes plant records, not database structure. A safe DML script identifies the intended rows, validates inputs, uses a transaction where several statements must succeed together, checks the affected-row count and confirms the final state.

  • Always name columns in production INSERT statements.
  • Preview an UPDATE or DELETE predicate with SELECT.
  • Use primary keys or approved business keys to target rows precisely.
  • Pass SCADA values as parameters rather than concatenating SQL strings.

SQL DML for SCADA and Industrial Automation

This guide covers SQL Server DML commands, SCADA SQL INSERT, safe UPDATE and DELETE, parameterized automation queries and a complete tblEvent CRUD lab.

1. What Is DML?

DML means Data Manipulation Language. It retrieves and changes the rows stored in tables or exposed through eligible views.

StatementPurposeAutomation example
SELECTRead rows and calculate resultsShow the latest furnace readings
INSERTAdd new rowsRecord a process event
UPDATEChange existing rowsCorrect an approved operator comment
DELETERemove selected rowsPurge an approved staging batch
MERGEConditionally insert, update or deleteSynchronize a controlled reference set

Some textbooks call SELECT DQL (Data Query Language), while SQL Server documentation commonly discusses it alongside data manipulation. The useful engineering distinction is simple: DML works with rows; DDL works with structure.

2. DML vs DDL, DCL and Transactions

GroupExamplesChanges
DML/DQLSELECT, INSERT, UPDATE, DELETERows and query results
DDLCREATE, ALTER, DROP, TRUNCATEDatabase structure
DCLGRANT, DENY, REVOKEPermissions
Transaction controlBEGIN TRANSACTION, COMMIT, ROLLBACKUnit-of-work boundaries
GO reminder: GO is an SSMS/sqlcmd batch separator, not a SQL Server statement. Do not include it in a command string sent through ODBC, ADO, Python or a WinCC database connection.

3. Inspect tblEvent Before Changing Data

Never assume the production table matches a training screenshot. Confirm the database, object, columns and a small sample first.

USE [SQF_DB];
GO

SELECT DB_NAME() AS [CurrentDatabase];

SELECT
    c.[column_id], c.[name] AS [ColumnName],
    t.[name] AS [DataType], c.[max_length],
    c.[precision], c.[scale], c.[is_nullable]
FROM sys.columns AS c
JOIN sys.types AS t ON t.[user_type_id] = c.[user_type_id]
WHERE c.[object_id] = OBJECT_ID(N'dbo.tblEvent')
ORDER BY c.[column_id];

SELECT TOP (10)
    [ID], [DT], [SQF_No], [ChargeNo],
    [Event_From], [Event_To], [Temp_Set], [Temp_Act], [Fan_Status]
FROM [dbo].[tblEvent]
ORDER BY [DT] DESC, [ID] DESC;
Expected: the current database is SQF_DB, the catalog lists the expected columns, and the sample is ordered newest first.

4. SELECT the Columns You Need

SELECT
    [ID], [DT], [SQF_No], [ChargeNo],
    [Temp_Set], [Temp_Act],
    ([Temp_Act] - [Temp_Set]) AS [TempDeviation],
    [Fan_Status]
FROM [dbo].[tblEvent];

Avoid SELECT * in stable SCADA interfaces. Explicit columns document the contract, reduce unnecessary network traffic and prevent a later schema change from silently altering column order or payload size.

An alias such as TempDeviation names the result expression; it does not add a column to the table.

5. Filter Rows with WHERE

DECLARE @EquipmentID int = 2;
DECLARE @FromUtc datetime2(3) = '2026-08-04T06:00:00.000';
DECLARE @ToUtc   datetime2(3) = '2026-08-04T14:00:00.000';

SELECT [ID], [DT], [ChargeNo], [Temp_Set], [Temp_Act]
FROM [dbo].[tblEvent]
WHERE [SQF_No] = @EquipmentID
  AND [DT] >= @FromUtc
  AND [DT] <  @ToUtc
ORDER BY [DT], [ID];

The half-open time range (>= start and < end) avoids double-counting a boundary when adjacent shifts or batches are queried.

NeedPredicate
Missing actual valueTemp_Act IS NULL
One of several equipment IDsSQF_No IN (1,2,3)
Charge prefixChargeNo LIKE 'CH-2026-%'
Temperature outside rangeTemp_Act NOT BETWEEN 0 AND 1500
Both conditions requiredUse parentheses with AND/OR

6. ORDER BY, TOP and Paging

Without ORDER BY, SQL Server does not promise row order. Add a unique tiebreaker such as ID when timestamps can repeat.

SELECT TOP (20)
    [ID], [DT], [SQF_No], [ChargeNo], [Event_To]
FROM [dbo].[tblEvent]
ORDER BY [DT] DESC, [ID] DESC;

-- Page 2 when each page contains 20 rows.
SELECT [ID], [DT], [SQF_No], [ChargeNo], [Event_To]
FROM [dbo].[tblEvent]
ORDER BY [DT] DESC, [ID] DESC
OFFSET 20 ROWS FETCH NEXT 20 ROWS ONLY;
TOP with UPDATE/DELETE is unordered: UPDATE TOP (100) or DELETE TOP (100) can choose any qualifying rows. If sequence matters, first select an ordered key set in a CTE/subquery, then modify those keys.

7. Join Event and Equipment Data

SELECT
    e.[ID], e.[DT], e.[ChargeNo],
    q.[EquipmentName],
    e.[Temp_Set], e.[Temp_Act], e.[Fan_Status]
FROM [dbo].[tblEvent] AS e
LEFT JOIN [dbo].[Equipment] AS q
    ON q.[EquipmentID] = e.[SQF_No]
WHERE e.[DT] >= DATEADD(HOUR, -8, SYSUTCDATETIME())
ORDER BY e.[DT] DESC, e.[ID] DESC;

INNER JOIN returns only matching equipment. LEFT JOIN retains every event and returns NULL for missing parent details, which is useful when diagnosing incomplete legacy mappings.

8. Aggregate Process Data with GROUP BY

SELECT
    [SQF_No],
    COUNT_BIG(*) AS [EventCount],
    MIN([DT]) AS [FirstEventUtc],
    MAX([DT]) AS [LastEventUtc],
    AVG(CONVERT(decimal(18,3), [Temp_Act])) AS [AverageTemp],
    MAX([Temp_Act]) AS [MaximumTemp]
FROM [dbo].[tblEvent]
WHERE [DT] >= DATEADD(DAY, -1, SYSUTCDATETIME())
GROUP BY [SQF_No]
ORDER BY [SQF_No];

COUNT(column) ignores NULL; COUNT(*) and COUNT_BIG(*) count rows. Aggregates such as AVG also ignore NULL, so report the valid-sample count when missing measurements matter.

SELECT
    [SQF_No],
    COUNT_BIG(*) AS [AllRows],
    COUNT([Temp_Act]) AS [ValidTemperatureRows],
    SUM(CASE WHEN [Temp_Act] IS NULL THEN 1 ELSE 0 END) AS [MissingTemperatureRows]
FROM [dbo].[tblEvent]
GROUP BY [SQF_No];

9. INSERT One Automation Event

Specify the target columns explicitly. Omit the identity column and let SQL Server generate it.

INSERT INTO [dbo].[tblEvent]
(
    [DT], [SQF_No], [ChargeNo], [Event_From], [Event_To],
    [Temp_Set], [Temp_Act], [Fan_Status]
)
VALUES
(
    SYSUTCDATETIME(), 2, 'CH-2026-0042', 'Heating', 'Soaking',
    950.000, 947.625, 1
);

Explicit column lists survive column reordering and make omitted default/nullable columns intentional. For Unicode columns, prefix literals with N, for example N'भट्ठी 2'.

10. INSERT Multiple Rows

INSERT INTO [dbo].[tblEvent]
    ([DT], [SQF_No], [ChargeNo], [Event_From], [Event_To], [Temp_Set], [Temp_Act], [Fan_Status])
VALUES
    ('2026-08-04T08:00:00.000', 2, 'CH-2026-0042', 'Load',    'Heating', 950.000, 120.500, 1),
    ('2026-08-04T08:30:00.000', 2, 'CH-2026-0042', 'Heating', 'Soaking', 950.000, 948.250, 1),
    ('2026-08-04T09:00:00.000', 2, 'CH-2026-0042', 'Soaking', 'Cooling', 950.000, 930.100, 0);

A multi-row constructor reduces round trips for small batches. For high-volume acquisition, use a staging table plus bulk loading, a table-valued parameter, or a purpose-built ingestion path rather than building one enormous SQL string.

11. Capture Identity and Changed Values with OUTPUT

The OUTPUT clause returns values from the rows affected by DML. It is more direct than running a separate query to find the inserted event.

INSERT INTO [dbo].[tblEvent]
    ([DT], [SQF_No], [ChargeNo], [Event_From], [Event_To], [Temp_Set], [Temp_Act], [Fan_Status])
OUTPUT
    inserted.[ID], inserted.[DT], inserted.[SQF_No], inserted.[ChargeNo]
VALUES
    (SYSUTCDATETIME(), 2, 'CH-2026-0043', 'Load', 'Heating', 925.000, 110.250, 1);
Expected: one result row returns the generated ID and stored values.

Use inserted for new values and deleted for old values in UPDATE/DELETE output.

12. INSERT...SELECT and Staging Tables

INSERT...SELECT moves a set of rows while keeping the operation inside SQL Server. Match columns by meaning and compatible data type—not by accidental position.

INSERT INTO [dbo].[tblEvent]
    ([DT], [SQF_No], [ChargeNo], [Event_From], [Event_To], [Temp_Set], [Temp_Act], [Fan_Status])
SELECT
    s.[EventTimeUtc], s.[EquipmentID], s.[BatchCode],
    s.[PreviousState], s.[NewState],
    s.[TemperatureSetpoint], s.[TemperatureActual], s.[FanOn]
FROM [dbo].[tblEvent_Stage] AS s
WHERE s.[ValidationStatus] = 'VALID'
  AND NOT EXISTS
  (
      SELECT 1
      FROM [dbo].[tblEvent] AS e
      WHERE e.[SQF_No] = s.[EquipmentID]
        AND e.[DT] = s.[EventTimeUtc]
        AND e.[ChargeNo] = s.[BatchCode]
  );

The NOT EXISTS check reduces duplicate loads, but concurrency still requires an enforced unique key or an appropriate serialized ingestion design.

13. UPDATE Rows Safely

Use the same predicate for preview and modification. Target the primary key whenever possible.

DECLARE @EventID int = 1250;

-- 1. Preview
SELECT [ID], [DT], [SQF_No], [ChargeNo], [Temp_Act]
FROM [dbo].[tblEvent]
WHERE [ID] = @EventID;

-- 2. Update exactly one event
UPDATE [dbo].[tblEvent]
SET [Temp_Act] = 948.875
WHERE [ID] = @EventID;

-- 3. Verify
SELECT [ID], [Temp_Act]
FROM [dbo].[tblEvent]
WHERE [ID] = @EventID;
Missing WHERE changes every row: SQL Server accepts UPDATE dbo.tblEvent SET Fan_Status = 0;. There is no automatic prompt asking whether you intended a full-table update.

14. UPDATE Using a Join

A joined update can apply approved reference data to matching rows. Preview the join first and ensure it does not duplicate the target row.

-- Preview the exact key/value pairs.
SELECT e.[ID], e.[SQF_No], e.[Event_To], m.[NormalizedState]
FROM [dbo].[tblEvent] AS e
JOIN [dbo].[StateMap] AS m
    ON m.[LegacyState] = e.[Event_To]
WHERE e.[DT] >= '2026-08-01T00:00:00';

-- Apply the approved mapping.
UPDATE e
SET e.[Event_To] = m.[NormalizedState]
FROM [dbo].[tblEvent] AS e
JOIN [dbo].[StateMap] AS m
    ON m.[LegacyState] = e.[Event_To]
WHERE e.[DT] >= '2026-08-01T00:00:00';

If more than one source row matches the same target, the chosen source value is not a safe business rule. Make the source key unique before running the update.

15. DELETE Only Approved Rows

DELETE removes rows, preserves the table and supports WHERE. Always preview and count the target set.

DECLARE @CutoffUtc datetime2(3) = '2025-08-01T00:00:00.000';

SELECT COUNT_BIG(*) AS [RowsProposedForDeletion]
FROM [dbo].[tblEvent_Stage]
WHERE [DT] < @CutoffUtc;

BEGIN TRANSACTION;

DELETE FROM [dbo].[tblEvent_Stage]
WHERE [DT] < @CutoffUtc;

SELECT @@ROWCOUNT AS [DeletedRows];

-- Use ROLLBACK during rehearsal; COMMIT only after approval and verification.
ROLLBACK TRANSACTION;
Retention is an engineering decision: consider audit, quality, regulatory, warranty, reporting and incident-analysis needs. Never use a generic time purge on event/alarm history without an approved retention and archive policy.

16. Capture Updated or Deleted Rows

Capture the exact before/after values in the same operation. This is useful for verification or an application response, but it does not replace a secured, complete audit architecture.

DECLARE @Changed table
(
    [ID] int,
    [OldTemp] decimal(9,3),
    [NewTemp] decimal(9,3),
    [ChangedUtc] datetime2(3)
);

UPDATE [dbo].[tblEvent]
SET [Temp_Act] = 949.125
OUTPUT
    inserted.[ID], deleted.[Temp_Act], inserted.[Temp_Act], SYSUTCDATETIME()
INTO @Changed ([ID], [OldTemp], [NewTemp], [ChangedUtc])
WHERE [ID] = 1250;

SELECT * FROM @Changed;
DELETE FROM [dbo].[tblEvent_Stage]
OUTPUT deleted.[ID], deleted.[DT], deleted.[SQF_No], deleted.[ChargeNo]
WHERE [LoadBatchID] = 9001
  AND [ValidationStatus] = 'REJECTED';

17. Transactions with TRY/CATCH

Use one transaction when several DML statements represent one business unit. Keep it short; open transactions retain locks and log records.

SET XACT_ABORT ON;
DECLARE @PreviousEventID int = 1249;

BEGIN TRY
    BEGIN TRANSACTION;

    INSERT INTO [dbo].[tblEvent]
        ([DT], [SQF_No], [ChargeNo], [Event_From], [Event_To], [Temp_Set], [Temp_Act], [Fan_Status])
    VALUES
        (SYSUTCDATETIME(), 2, 'CH-2026-0044', 'Heating', 'Soaking', 950.000, 949.000, 1);

    UPDATE [dbo].[tblEvent]
    SET [Event_To] = 'Soaking'
    WHERE [ID] = @PreviousEventID
      AND [SQF_No] = 2;

    IF @@ROWCOUNT <> 1
        THROW 50001, 'Expected one previous event row.', 1;

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
    THROW;
END CATCH;

XACT_ABORT ON helps ensure many runtime errors terminate and roll back the transaction. TRY/CATCH lets the script explicitly handle and rethrow errors.

18. Validate the Affected-Row Count

For a key-based operator action, zero rows and two rows can both be failures. Store @@ROWCOUNT immediately because the next statement can change it.

DECLARE @EventID int = 1250;
DECLARE @Rows int;

UPDATE [dbo].[tblEvent]
SET [Fan_Status] = 0
WHERE [ID] = @EventID;

SET @Rows = @@ROWCOUNT;

IF @Rows <> 1
    THROW 50002, 'Expected to update exactly one tblEvent row.', 1;

For optimistic concurrency, include the original value or a rowversion token in the predicate. A zero-row result then means another writer changed the row or the row no longer exists.

19. Upsert and Duplicate Protection

An upsert inserts a missing row or updates an existing one. Define the business key first and enforce it with a unique constraint. For simple cases, explicit UPDATE-then-INSERT logic is easier to reason about than a complex MERGE.

SET XACT_ABORT ON;
BEGIN TRANSACTION;

UPDATE [dbo].[Equipment] WITH (UPDLOCK, SERIALIZABLE)
SET [EquipmentName] = @EquipmentName,
    [IsActive] = @IsActive
WHERE [EquipmentID] = @EquipmentID;

IF @@ROWCOUNT = 0
BEGIN
    INSERT INTO [dbo].[Equipment]
        ([EquipmentID], [EquipmentName], [IsActive])
    VALUES
        (@EquipmentID, @EquipmentName, @IsActive);
END;

COMMIT TRANSACTION;
Retry design: networks can fail after SQL Server commits but before Runtime receives confirmation. Use a source event ID or other idempotency key so a retry does not create a duplicate process event.

20. NULL, DEFAULT and Conversion Rules

  • NULL means unknown, missing or not applicable; test with IS NULL.
  • Omitting a column can activate its default; explicitly inserting NULL does not request the default.
  • COALESCE selects the first non-null expression, but do not hide missing sensor quality unintentionally.
  • Use TRY_CONVERT when profiling untrusted staging text; failed conversions return NULL.
  • Use ISO 8601 timestamps and an explicit UTC policy for system integration.
SELECT
    [ID],
    [Temp_Act],
    CASE
        WHEN [Temp_Act] IS NULL THEN 'MISSING'
        WHEN [Temp_Act] > 1200 THEN 'HIGH'
        ELSE 'NORMAL'
    END AS [TemperatureQuality]
FROM [dbo].[tblEvent];

SELECT [RawTemp]
FROM [dbo].[tblEvent_Stage]
WHERE [RawTemp] IS NOT NULL
  AND TRY_CONVERT(decimal(9,3), [RawTemp]) IS NULL;

21. Use Parameterized SCADA Commands

Do not concatenate tag values, batch numbers or operator text into SQL syntax. Use placeholders supported by the client library and bind values with matching SQL types and sizes.

-- SQL text sent by a named-parameter client
INSERT INTO dbo.tblEvent
    (DT, SQF_No, ChargeNo, Event_From, Event_To, Temp_Set, Temp_Act, Fan_Status)
OUTPUT inserted.ID
VALUES
    (@DT, @SQF_No, @ChargeNo, @Event_From, @Event_To,
     @Temp_Set, @Temp_Act, @Fan_Status);
SCADA valueSuggested SQL parameterValidation
Equipment numberintExists in Equipment
Event timestampdatetime2(3)UTC and within acceptable clock window
Charge codevarchar(50)Length and approved character policy
Temperaturedecimal(9,3)Engineering range and quality
Fan statebit0/1 or null according to the process model

Parameters reduce SQL-injection risk and conversion errors, but validation and least privilege still matter. Do not give a Runtime account ownership or unrestricted write access.

22. Put the Write Contract in a Stored Procedure

A stored procedure centralizes validation, transaction behavior and permissions. Runtime can receive EXECUTE permission without direct update/delete rights on every table.

CREATE OR ALTER PROCEDURE [dbo].[usp_RecordEvent]
    @DT          datetime2(3),
    @SQF_No      int,
    @ChargeNo    varchar(50),
    @Event_From  varchar(100),
    @Event_To    varchar(100),
    @Temp_Set    decimal(9,3) = NULL,
    @Temp_Act    decimal(9,3) = NULL,
    @Fan_Status  bit = NULL
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;

    IF NOT EXISTS (SELECT 1 FROM dbo.Equipment WHERE EquipmentID = @SQF_No AND IsActive = 1)
        THROW 50010, 'Equipment is missing or inactive.', 1;

    INSERT INTO dbo.tblEvent
        (DT, SQF_No, ChargeNo, Event_From, Event_To, Temp_Set, Temp_Act, Fan_Status)
    OUTPUT inserted.ID, inserted.DT
    VALUES
        (@DT, @SQF_No, @ChargeNo, @Event_From, @Event_To, @Temp_Set, @Temp_Act, @Fan_Status);
END;
GO

This procedure definition is DDL; calling it with EXEC dbo.usp_RecordEvent ... performs DML. Applications still bind its parameter values rather than concatenating an EXEC string.

23. Batch Large DML Operations Deterministically

Large updates/deletes can block acquisition clients and grow the transaction log. Process a measured number of keys per transaction and make the selection order explicit.

WHILE 1 = 1
BEGIN
    ;WITH Batch AS
    (
        SELECT TOP (1000) [ID]
        FROM [dbo].[tblEvent_Stage]
        WHERE [ValidationStatus] = 'PROCESSED'
          AND [DT] < @CutoffUtc
        ORDER BY [ID]
    )
    DELETE s
    FROM [dbo].[tblEvent_Stage] AS s
    JOIN Batch AS b ON b.[ID] = s.[ID];

    IF @@ROWCOUNT = 0 BREAK;
END;

Batch size is workload-specific. Monitor transaction-log usage, lock waits, Runtime latency and execution time. An index supporting the batch predicate/order is usually essential.

24. Common DML Errors and Fixes

SymptomLikely causeCheck or fix
String/binary data would be truncatedValue exceeds column/parameter sizeValidate length; do not silently cut plant identifiers
Conversion failedText cannot convert to numeric/date typeProfile staging data with TRY_CONVERT
Foreign key conflictEquipment ID does not existSynchronize reference data before the event write
Unique-key violationDuplicate natural/idempotency keyTreat as retry or data-quality issue according to design
Zero rows updatedWrong key, concurrent change or missing rowCheck predicate and concurrency token
Too many rows updated/deletedPredicate too broadRoll back; compare preview key set and affected count
Deadlock victimConflicting access orderShorten transactions, use consistent order/indexes and bounded retry
Timeout expiredBlocking, scan, slow network or insufficient timeoutMeasure waits/plan; do not blindly increase timeout
Syntax error near GOApplication sent a client batch separatorRemove GO or split batches client-side

25. Complete Practical DML Lab

Hands-on
Lab safety
  • Use only a disposable copy of SQF_DB.
  • Complete the companion DDL lab first.
  • Run destructive steps inside a transaction and roll back during rehearsal.
1

Insert three related events

Insert Load→Heating, Heating→Soaking and Soaking→Cooling events for one charge. Use an explicit column list and OUTPUT inserted.ID.

Expected: three generated event IDs are returned.
2

Query the charge timeline

SELECT [ID], [DT], [SQF_No], [Event_From], [Event_To], [Temp_Set], [Temp_Act]
FROM [dbo].[tblEvent]
WHERE [ChargeNo] = 'CH-LAB-0001'
ORDER BY [DT], [ID];
Expected: events appear in timestamp and identity order.
3

Correct one row with optimistic verification

Preview the row by ID, update its actual temperature, capture old/new values with OUTPUT and require @@ROWCOUNT = 1.

4

Calculate charge statistics

SELECT
    [ChargeNo], COUNT_BIG(*) AS [Events],
    MIN([DT]) AS [StartedUtc], MAX([DT]) AS [LastEventUtc],
    MIN([Temp_Act]) AS [MinTemp], MAX([Temp_Act]) AS [MaxTemp],
    AVG(CONVERT(decimal(18,3), [Temp_Act])) AS [AvgTemp]
FROM [dbo].[tblEvent]
WHERE [ChargeNo] = 'CH-LAB-0001'
GROUP BY [ChargeNo];
5

Rehearse a delete and rollback

BEGIN TRANSACTION;

DELETE FROM [dbo].[tblEvent]
OUTPUT deleted.[ID], deleted.[DT], deleted.[ChargeNo]
WHERE [ChargeNo] = 'CH-LAB-0001';

-- Confirm the rows are absent inside this transaction.
SELECT COUNT_BIG(*) AS [RowsRemaining]
FROM [dbo].[tblEvent]
WHERE [ChargeNo] = 'CH-LAB-0001';

ROLLBACK TRANSACTION;

-- Confirm rollback restored the rows.
SELECT COUNT_BIG(*) AS [RowsAfterRollback]
FROM [dbo].[tblEvent]
WHERE [ChargeNo] = 'CH-LAB-0001';
Expected: zero rows remain before rollback; the three lab rows return after rollback.
6

Design the Runtime command

Convert the insert into a parameterized call to dbo.usp_RecordEvent. Define parameter types/sizes, timeout, one retry policy and a source event ID that prevents duplicate records.

Lab complete

You can now query event histories, insert with identity confirmation, update an exact row, aggregate process data, rehearse deletion safely and design a parameterized SCADA write contract.

DML Safety Checklist

  1. Confirm server, database, schema and table.
  2. Use explicit columns and qualified object names.
  3. Validate source values, units, timestamp policy and tag quality.
  4. Preview target keys with SELECT.
  5. Use parameters or a stored procedure.
  6. Start a short transaction when statements form one unit.
  7. Capture OUTPUT or store @@ROWCOUNT immediately.
  8. Verify the result before commit.
  9. Log the operation without exposing credentials or sensitive values.
  10. Test retry, timeout, disconnect and duplicate-event behavior.

Further Reading

Frequently Asked Questions

What is DML in SQL Server?

DML reads and changes table rows. The core statements are SELECT, INSERT, UPDATE and DELETE; MERGE is also DML.

How do I safely update automation data?

Preview the exact key set, use a precise WHERE clause, execute inside a transaction when appropriate, check the affected-row count and verify before commit.

How do I return a newly inserted identity value?

Use OUTPUT inserted.ID inside the INSERT statement. It can return additional stored values in the same result.

Why should SCADA scripts use parameters?

Parameters keep values separate from SQL syntax, reduce injection risk and provide predictable type conversion. They should be combined with validation and minimum permissions.

Does UPDATE TOP process the oldest rows first?

No. TOP in UPDATE or DELETE does not define order. Select an ordered key set in a CTE or subquery, then modify those keys.

Get the SQL Reporting and Automation Syllabus

Share your details and a Softwell advisor will contact you with practical SQL, SCADA and industrial reporting training options.

Build reliable SQL data workflows for automation

Join practical online, classroom or corporate SQL Server, reporting and SCADA integration training.

Request Course Details
Verified learning pathway

Discuss SQL DML and Automation Data Training

Explore practical SQL Server, PLC/SCADA integration and industrial data training options.

Content reviewed: 4 August 2026

☎ Call WhatsApp ✉ Email Enquire Now