Showing posts with label T-SQL. Show all posts
Showing posts with label T-SQL. Show all posts

Monday, August 5, 2024

Parameterized Queries in SQL Server

 In SQL Server, parameterized queries can be used in various ways, including through stored procedures, prepared statements, and the use of parameters in direct SQL commands. Here are some examples demonstrating different methods for creating parameterized queries in SQL Server.

Using Stored Procedures

Stored procedures are a common way to use parameterized queries in SQL Server. Here's an example:

Creating a Stored Procedure

First, create a stored procedure in SQL Server:

sql
CREATE PROCEDURE InsertUser
    @Name NVARCHAR(50),
    @Age INT
AS
BEGIN
    INSERT INTO Users (Name, Age)
    VALUES (@Name, @Age)
END

Executing the Stored Procedure

You can execute the stored procedure from SQL Server Management Studio (SSMS) or from an application using a parameterized query.

From SSMS:

sql
EXEC InsertUser @Name = 'Alice', @Age = 30;
EXEC InsertUser @Name = 'Bob', @Age = 25;

Using ADO.NET in C#

Here is an example of using parameterized queries with ADO.NET in a C# application:

csharp
using System;
using System.Data;
using System.Data.SqlClient;

class Program
{
    static void Main()
    {
        string connectionString = "your_connection_string_here";
        
        using (SqlConnection conn = new SqlConnection(connectionString))
        {
            conn.Open();
            
            // Insert user
            using (SqlCommand cmd = new SqlCommand("INSERT INTO Users (Name, Age) VALUES (@Name, @Age)", conn))
            {
                cmd.Parameters.Add("@Name", SqlDbType.NVarChar).Value = "Alice";
                cmd.Parameters.Add("@Age", SqlDbType.Int).Value = 30;
                cmd.ExecuteNonQuery();
            }
            
            // Select users
            using (SqlCommand cmd = new SqlCommand("SELECT * FROM Users WHERE Age > @Age", conn))
            {
                cmd.Parameters.Add("@Age", SqlDbType.Int).Value = 20;
                
                using (SqlDataReader reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Console.WriteLine($"ID: {reader["ID"]}, Name: {reader["Name"]}, Age: {reader["Age"]}");
                    }
                }
            }
        }
    }
}

Using SQL Server Management Studio (SSMS)

You can also use parameterized queries directly in SSMS by declaring and setting variables:

sql
DECLARE @Name NVARCHAR(50);
DECLARE @Age INT;

SET @Name = 'Alice';
SET @Age = 30;

INSERT INTO Users (Name, Age)
VALUES (@Name, @Age);

SET @Name = 'Bob';
SET @Age = 25;

INSERT INTO Users (Name, Age)
VALUES (@Name, @Age);

DECLARE @AgeThreshold INT;
SET @AgeThreshold = 20;

SELECT * FROM Users
WHERE Age > @AgeThreshold;

Benefits of Using Parameterized Queries in SQL Server

  1. Security: Parameterized queries help prevent SQL injection attacks by ensuring that user input is treated as data rather than executable code.
  2. Performance: SQL Server can cache execution plans for parameterized queries, improving the performance of repeated query execution.
  3. Readability and Maintainability: Separating query logic from data makes the code easier to read and maintain.

Using parameterized queries in SQL Server, whether through stored procedures, application code, or directly in SSMS, is a best practice that enhances the security and performance of your database operations.

Thursday, August 1, 2024

Pivoting and unpivoting data in T-SQL

 Pivoting and unpivoting data in T-SQL can be extremely useful for transforming your data into different shapes, particularly for reporting and analysis. Here's an overview of how to use the PIVOT and UNPIVOT operators in T-SQL.

Pivot

The PIVOT operator transforms rows into columns. This is particularly useful for creating summary tables or cross-tab reports.

Example: Pivoting Data

Let's assume we have a table named Sales with the following data:

YearQuarterSalesAmount
2022Q1100
2022Q2150
2022Q3200
2022Q4250
2023Q1110
2023Q2160
2023Q3210
2023Q4260

We want to pivot this data so that each quarter becomes a column.

SELECT Year, [Q1], [Q2], [Q3], [Q4]
FROM
(
    SELECT Year, Quarter, SalesAmount
    FROM Sales
) AS SourceTable
PIVOT
(
    SUM(SalesAmount)
    FOR Quarter IN ([Q1], [Q2], [Q3], [Q4])
) AS PivotTable
ORDER BY Year;

Result:

YearQ1Q2Q3Q4
2022100150200250
2023110160210260

Unpivot

The UNPIVOT operator performs the reverse transformation, turning columns into rows. This is useful for normalizing data or preparing it for certain types of analysis.

Example: Unpivoting Data

Suppose we have a table named QuarterlySales with the following data:

YearQ1Q2Q3Q4
2022100150200250
2023110160210260

We want to unpivot this data so that each quarter becomes a row.

SELECT Year, Quarter, SalesAmount
FROM
(
    SELECT Year, [Q1], [Q2], [Q3], [Q4]
    FROM QuarterlySales
) AS SourceTable
UNPIVOT
(
    SalesAmount FOR Quarter IN ([Q1], [Q2], [Q3], [Q4])
) AS UnpivotTable
ORDER BY Year, Quarter;

Result:

YearQuarterSalesAmount
2022Q1100
2022Q2150
2022Q3200
2022Q4250
2023Q1110
2023Q2160
2023Q3210
2023Q4260

Explanation

Pivot

  • Source Table: The inner query selects the source data.
  • PIVOT: The PIVOT clause aggregates the SalesAmount values and spreads them across new columns specified in the FOR Quarter IN ([Q1], [Q2], [Q3], [Q4]) clause.
  • SUM(SalesAmount): This is the aggregation function applied to the SalesAmount values.

Unpivot

  • Source Table: The inner query selects the source data.
  • UNPIVOT: The UNPIVOT clause transforms the column values into rows. The FOR Quarter IN ([Q1], [Q2], [Q3], [Q4]) clause specifies the columns to be unpivoted.
  • SalesAmount FOR Quarter: This specifies the new column names for the values (SalesAmount) and the labels (Quarter).

Practical Considerations

  • Performance: Pivoting and unpivoting can be resource-intensive, especially on large datasets. Indexing and query optimization are essential.
  • Data Integrity: Ensure that the data being pivoted or unpivoted is clean and consistent to avoid unexpected results.
  • Dynamic Pivoting: For scenarios where the columns to be pivoted are not known in advance, dynamic SQL can be used to construct the pivot query at runtime.

These techniques are powerful tools in T-SQL for data transformation, making it easier to reshape data for analysis and reporting.

Dynamic Pivoting in t-sql

 Dynamic pivoting in T-SQL allows you to create pivot tables where the columns are not known beforehand and need to be generated dynamically based on the data. This is useful when the set of values to be pivoted changes frequently or is not known at compile time.

Here's a step-by-step example to demonstrate dynamic pivoting in T-SQL:

Scenario

Let's say we have a Sales table with the following data:

YearQuarterSalesAmount
2022Q1100
2022Q2150
2022Q3200
2022Q4250
2023Q1110
2023Q2160
2023Q3210
2023Q4260

We want to dynamically pivot this data so that each quarter becomes a column.

Steps for Dynamic Pivoting

  1. Retrieve the unique values for the columns to pivot.
  2. Construct the dynamic SQL query.
  3. Execute the dynamic SQL query.

Step 1: Retrieve Unique Column Values

First, we need to get the unique values from the Quarter column:

DECLARE @Columns NVARCHAR(MAX);
SELECT @Columns = STRING_AGG(QUOTENAME(Quarter), ',') 
FROM (SELECT DISTINCT Quarter FROM Sales) AS Quarters;

Step 2: Construct the Dynamic SQL Query

Next, we construct the dynamic SQL query using the retrieved column values:

SET @SQL = '
SELECT Year, ' + @Columns + '
FROM
(
    SELECT Year, Quarter, SalesAmount
    FROM Sales
) AS SourceTable
PIVOT
(
    SUM(SalesAmount)
    FOR Quarter IN (' + @Columns + ')
) AS PivotTable
ORDER BY Year;
';

Step 3: Execute the Dynamic SQL Query

Finally, execute the dynamic SQL query:


EXEC sp_executesql @SQL;

Complete Example

Putting it all together, the complete T-SQL script looks like this:

-- Step 1: Retrieve unique column values
DECLARE @Columns NVARCHAR(MAX);
SELECT @Columns = STRING_AGG(QUOTENAME(Quarter), ',') 
FROM (SELECT DISTINCT Quarter FROM Sales) AS Quarters;

-- Step 2: Construct the dynamic SQL query
DECLARE @SQL NVARCHAR(MAX);

SET @SQL = '
SELECT Year, ' + @Columns + '
FROM
(
    SELECT Year, Quarter, SalesAmount
    FROM Sales
) AS SourceTable
PIVOT
(
    SUM(SalesAmount)
    FOR Quarter IN (' + @Columns + ')
) AS PivotTable
ORDER BY Year;
';

-- Step 3: Execute the dynamic SQL query
EXEC sp_executesql @SQL;

Explanation

  1. Retrieve Unique Column Values: The STRING_AGG function is used to concatenate the distinct quarter values, separated by commas. The QUOTENAME function ensures that the column names are properly escaped.

  2. Construct the Dynamic SQL Query: The @Columns variable is used to dynamically insert the column names into the PIVOT clause of the SQL query.

  3. Execute the Dynamic SQL Query: The sp_executesql stored procedure is used to execute the dynamically constructed SQL query.

This approach allows you to pivot data dynamically based on the actual values present in the dataset, making it flexible and adaptable to changing data.

Pivoting - tricky example in t-sql

 Let's create a tricky pivoting example in T-SQL that involves more complex transformations, such as incorporating additional calculations and handling potential NULL values.

Scenario

Suppose we have a Sales table with the following structure and data:

YearQuarterRegionSalesAmount
2022Q1East100
2022Q1West200
2022Q2East150
2022Q2West250
2022Q3East200
2022Q3West300
2022Q4East250
2022Q4West350
2023Q1East110
2023Q1West210
2023Q2East160
2023Q2West260
2023Q3East210
2023Q3West310
2023Q4East260
2023Q4West360

We want to pivot this data to show the total sales for each region by year, and include columns for the percentage of total sales for each quarter within each year.

Pivoting with Calculations Example

Step 1: Create a Temporary Table for Summed Data

First, create a temporary table that includes the total sales for each quarter by region and the overall total sales for each year.

-- Create a temporary table to store summed data
SELECT 
    Year, 
    Quarter, 
    Region, 
    SalesAmount,
    SUM(SalesAmount) OVER (PARTITION BY Year) AS TotalSalesYear
INTO #SalesSummary
FROM Sales;

Step 2: Construct the Dynamic SQL Query for Pivoting

Construct the dynamic SQL query to pivot the data and include the percentage calculations.

DECLARE @Columns NVARCHAR(MAX);
DECLARE @SQL NVARCHAR(MAX);

-- Get unique regions and construct column names
SELECT @Columns = STRING_AGG(QUOTENAME(Region), ', ')
FROM (SELECT DISTINCT Region FROM Sales) AS Regions;

-- Construct the dynamic SQL query
SET @SQL = '
SELECT Year, ' + @Columns + ',
    ' + @Columns + 'Q1_Percent, ' + @Columns + 'Q2_Percent, ' + @Columns + 'Q3_Percent, ' + @Columns + 'Q4_Percent
FROM
(
    SELECT 
        Year, 
        Region, 
        SUM(SalesAmount) AS TotalSales,
        SUM(CASE WHEN Quarter = ''Q1'' THEN SalesAmount END) AS Q1_Sales,
        SUM(CASE WHEN Quarter = ''Q2'' THEN SalesAmount END) AS Q2_Sales,
        SUM(CASE WHEN Quarter = ''Q3'' THEN SalesAmount END) AS Q3_Sales,
        SUM(CASE WHEN Quarter = ''Q4'' THEN SalesAmount END) AS Q4_Sales,
        SUM(SalesAmount) * 100.0 / NULLIF(SUM(SUM(SalesAmount)) OVER (PARTITION BY Year), 0) AS TotalSalesPercent
    FROM #SalesSummary
    GROUP BY Year, Region
) AS SourceTable
PIVOT
(
    MAX(TotalSales) FOR Region IN (' + @Columns + ')
) AS PivotTable
PIVOT
(
    MAX(TotalSalesPercent) FOR Region IN (' + @Columns + 'Q1_Percent, ' + @Columns + 'Q2_Percent, ' + @Columns + 'Q3_Percent, ' + @Columns + 'Q4_Percent)
) AS PercentTable
ORDER BY Year;
';

-- Execute the dynamic SQL query
EXEC sp_executesql @SQL;

Explanation

  1. Temporary Table with Summed Data: The #SalesSummary table includes the total sales for each quarter by region and the overall total sales for each year. The SUM(SalesAmount) OVER (PARTITION BY Year) calculates the total sales for the entire year for use in the percentage calculation.

  2. Dynamic Columns: The STRING_AGG function constructs a comma-separated list of regions to dynamically generate the pivot column names.

  3. Dynamic SQL Query: The @SQL variable contains the dynamic SQL query that performs the pivot operation and includes additional calculations for each quarter's sales percentage within the year. The CASE statements within the query handle the different quarters, and the NULLIF function prevents division by zero errors.

  4. Pivoting and Calculations: The PIVOT operation is used twice:

    • First, to pivot the total sales data.
    • Second, to pivot the percentage calculations for each quarter.
  5. Execution: The sp_executesql stored procedure executes the dynamically constructed SQL query.

This example showcases how to dynamically pivot data while incorporating additional calculations, handling potential NULL values, and generating the pivot columns based on the data at runtime.

Dynamic SQL in t-sql

 Dynamic SQL in T-SQL (Transact-SQL, used in Microsoft SQL Server) is used to construct and execute SQL statements at runtime. This is particularly useful when the SQL query needs to be generated dynamically based on varying conditions, user inputs, or other runtime parameters.

How to Write and Execute Dynamic SQL in T-SQL

There are two primary ways to execute dynamic SQL in T-SQL:

  1. Using EXEC: The simplest way to execute a dynamic SQL statement is by using the EXEC command.
  2. Using sp_executesql: This method is more flexible and secure, as it allows parameterization of the dynamic SQL, which helps prevent SQL injection.

Examples

Example using EXEC

DECLARE @TableName NVARCHAR(50)
DECLARE @SQL NVARCHAR(MAX)

SET @TableName = N'Employees'
SET @SQL = N'SELECT * FROM ' + @TableName

EXEC(@SQL)

Example using sp_executesql

DECLARE @TableName NVARCHAR(50)
DECLARE @SQL NVARCHAR(MAX)
DECLARE @Column NVARCHAR(50)
DECLARE @Value NVARCHAR(50)

SET @TableName = N'Employees'
SET @Column = N'LastName'
SET @Value = N'Smith'
SET @SQL = N'SELECT * FROM ' + @TableName + ' WHERE ' + @Column + ' = @Value'

EXEC sp_executesql @SQL, N'@Value NVARCHAR(50)', @Value

Using Parameters to Prevent SQL Injection

One of the main benefits of using sp_executesql over EXEC is the ability to parameterize the SQL query, which helps prevent SQL injection attacks. Here’s an example:

DECLARE @TableName NVARCHAR(50)
DECLARE @SQL NVARCHAR(MAX)
DECLARE @LastName NVARCHAR(50)

SET @TableName = N'Employees'
SET @LastName = N'Smith'
SET @SQL = N'SELECT * FROM ' + @TableName + ' WHERE LastName = @LastName'

EXEC sp_executesql @SQL, N'@LastName NVARCHAR(50)', @LastName

Dynamic SQL with OUTPUT Parameters

sp_executesql also supports output parameters. Here’s an example of using an output parameter:

DECLARE @SQL NVARCHAR(MAX)
DECLARE @AvgSalary MONEY
DECLARE @DepartmentID INT

SET @DepartmentID = 1
SET @SQL = N'SELECT @AvgSalary = AVG(Salary) FROM Employees WHERE DepartmentID = @DeptID'

EXEC sp_executesql @SQL, N'@DeptID INT, @AvgSalary MONEY OUTPUT', @DeptID = @DepartmentID, @AvgSalary = @AvgSalary OUTPUT

PRINT @AvgSalary

Best Practices for Using Dynamic SQL in T-SQL

  1. Use sp_executesql with Parameters: Always use sp_executesql with parameters to avoid SQL injection.
  2. Validate Input: Validate all user inputs and ensure they are properly sanitized before including them in dynamic SQL.
  3. Use QUOTENAME: When dynamically constructing SQL object names (like table or column names), use the QUOTENAME function to properly escape them and prevent SQL injection.
  4. Limit the Use of Dynamic SQL: Use dynamic SQL only when necessary. Static SQL is easier to read, maintain, and optimize.

Here’s an example that demonstrates the use of QUOTENAME:

DECLARE @TableName NVARCHAR(50)
DECLARE @SQL NVARCHAR(MAX)

SET @TableName = N'Employees'
SET @SQL = N'SELECT * FROM ' + QUOTENAME(@TableName)

EXEC sp_executesql @SQL

By following these best practices, you can safely and effectively use dynamic SQL in T-SQL to handle complex querying requirements that static SQL cannot easily address.

Dynamic SQL example in t-SQL

Dynamic SQL example in T-SQL often involves constructing complex queries that incorporate multiple tables, conditional logic, and parameterization to ensure both flexibility and security. Below is an example that demonstrates these concepts. This example involves dynamically constructing a query to retrieve data from multiple tables based on various conditions and parameters, while also preventing SQL injection.

Scenario

Let's say we have a database with the following tables:

  • Employees
  • Departments
  • Projects

We want to create a dynamic SQL query that:

  1. Retrieves employee details.
  2. Includes department names if a specific flag is set.
  3. Filters results based on employee status and department ID.
  4. Joins with the Projects table if a project filter is provided.

Tables Structure

CREATE TABLE Employees (
    EmployeeID INT PRIMARY KEY,
    FirstName NVARCHAR(50),
    LastName NVARCHAR(50),
    DepartmentID INT,
    Status NVARCHAR(20)
);

CREATE TABLE Departments (
    DepartmentID INT PRIMARY KEY,
    DepartmentName NVARCHAR(50)
);

CREATE TABLE Projects (
    ProjectID INT PRIMARY KEY,
    ProjectName NVARCHAR(50),
    EmployeeID INT
);

Dynamic SQL Example

DECLARE @IncludeDepartment BIT = 1
DECLARE @Status NVARCHAR(20) = 'Active'
DECLARE @DepartmentID INT = 2
DECLARE @IncludeProjects BIT = 1
DECLARE @SQL NVARCHAR(MAX)
DECLARE @Params NVARCHAR(MAX)

-- Start constructing the SQL query
SET @SQL = N'SELECT E.EmployeeID, E.FirstName, E.LastName, E.Status'

-- Conditionally include department information
IF @IncludeDepartment = 1
BEGIN
    SET @SQL = @SQL + N', D.DepartmentName'
END

-- Join the Employees table
SET @SQL = @SQL + N' FROM Employees E'

-- Conditionally join the Departments table
IF @IncludeDepartment = 1
BEGIN
    SET @SQL = @SQL + N' LEFT JOIN Departments D ON E.DepartmentID = D.DepartmentID'
END

-- Add WHERE clause for filtering by status and department
SET @SQL = @SQL + N' WHERE E.Status = @Status AND E.DepartmentID = @DepartmentID'

-- Conditionally join the Projects table and add filter
IF @IncludeProjects = 1
BEGIN
    SET @SQL = @SQL + N' AND EXISTS (SELECT 1 FROM Projects P WHERE P.EmployeeID = E.EmployeeID)'
END

-- Define the parameters
SET @Params = N'@Status NVARCHAR(20), @DepartmentID INT'

-- Execute the dynamic SQL
EXEC sp_executesql @SQL, @Params, @Status = @Status, @DepartmentID = @DepartmentID

Explanation

  1. Conditional Columns and Joins: The query dynamically includes the DepartmentName column and joins with the Departments table if @IncludeDepartment is set to 1. Similarly, it includes a condition to join with the Projects table if @IncludeProjects is set to 1.

  2. Parameterization: The query uses parameters (@Status and @DepartmentID) to safely include user inputs and prevent SQL injection.

  3. Complex Logic: The query incorporates conditional logic to construct different parts of the SQL statement based on the provided parameters.

  4. Execution: The sp_executesql stored procedure is used to execute the constructed SQL statement with the defined parameters.

This example demonstrates how to build a flexible and secure dynamic SQL query that can adapt to various runtime conditions while maintaining readability and security.