Wednesday, 4 June 2014

Some Handy TSQL Queries for Testing

Following are some handy SQL queries which will be useful in your day to day testing activities. These queries are taken from MS book for TSQL - "Querying Microsoft SQL Server 2012". I haven't explained much here as the syntax are mostly self explanatory. If required you may drop your queries which I'll try to answer.

-- Logical Query Processing: Logically the following query is processed as FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY

SELECT country, YEAR(hiredate) AS yearhired, Count(*) AS [count]
FROM HR.Employees
WHERE hiredate >= '20030101'
GROUP BY country, Year(hiredate)
HAVING Count(*) > 1
ORDER BY country, yearhired;

--Declare Variable
DECLARE @f AS INT = 10;
SELECT @f AS value;

-- Date/Time Important Type & Functions
SELECT CAST(SYSDATETIME() AS DATE)AS today;
SELECT CAST(SYSDATETIME() AS TIME)AS timenow;
SELECT DATENAME(Month,SYSDATETIME()) AS thisMonth;
SELECT DATENAME(DAY,SYSDATETIME()) AS thisDay;
SELECT DATENAME(HOUR,SYSDATETIME()) AS thisHour;

--Concatenation: NULL in any value will return NULL
SELECT empid, country, region, city,
      country + N',' + region + N',' + city AS location
FROM HR.Employees;

--Concatenation: Replace NULL region with space
SELECT empid, country, region, city,
      country + COALESCE(N',' + region, N' ') + N',' + city AS location
FROM HR.Employees;

-- ISNULL: Only Diff with COALESCE - Type of return is determined by the first input
SELECT empid, country, region, city,
      country + ISNULL(N',' + region, N' ') + N',' + city AS location
FROM HR.Employees;

--Other Important Functions:
SELECT SUBSTRING('ABCDE',1,3) AS string;              -- Returns ‘ABC’
SELECT SUBSTRING('ABCDE',1,50) AS string;             -- Returns ‘ABCDE’
SELECT LEFT('ABCDE',3);                               -- Returns ‘ABC’
SELECT RIGHT('ABCDE',3);                              -- Returns ‘CDE’
SELECT CHARINDEX(' ', 'Itzik Ben-Gan');               -- Returns 6
SELECT PATINDEX ('%[0-9]%', 'abcd123efgh') AS position;     -- Returns 5
SELECT LEN(N'xyz ') AS length1;                       -- Returns 3
SELECT LEN('xyz ') AS length2;                        -- Returns 3
SELECT DATALENGTH(N'xyz ') AS length3; -- Returns 8 (2 bytes/char + 2 bytes/space)
SELECT DATALENGTH('xyz ') AS length4;     -- Returns 4 (1 byte/char + 1 byte/space)
SELECT REPLACE ('1.2.3', '.', '/')  AS replaced;      -- Returns 1/2/3
SELECT REPLICATE ('*',10)  AS asterisk;         -- Returns ‘**********’
SELECT STUFF('ABCDEF',1,3,'PQR') AS stuffed;    -- Returns ‘PQRDEF’
SELECT LOWER('ABCDEF') AS lowered;              -- Returns ‘abcdef’
SELECT UPPER('abcdef') AS uppered;              -- Returns ‘ABCDEF’
SELECT LTRIM(' ABCDEF ') AS ltrimed;            -- Returns ‘ABCDEF  ’
SELECT RTRIM(' ABCDEF ') AS rtrimed;            -- Returns ‘ ABCDEF’
SELECT LTRIM(RTRIM(' ABCDEF ')) AS trimed;      -- Returns ‘ABCDEF’
SELECT NULLIF('ABC','ABC') AS [NULLIF];         -- Equal Returns NULL
SELECT NULLIF('ABC',NULL) AS [NULLIF];          -- Returns 'ABC' 
SELECT NULLIF(NULL,NULL) AS [NULLIF];           -- Error


--Simple Case:
SELECT productid, productname, unitprice, discontinued,
      CASE discontinued
            WHEN 0 THEN 'yes'
            WHEN 1 THEN 'no'
            ELSE 'unknown'
      END AS discontinued_desc     
FROM Production.Products;

--Searched form Case:
SELECT productid, productname, unitprice, discontinued,
      CASE
            WHEN unitprice < 20.00 THEN 'low'
            WHEN unitprice < 40.00 THEN 'Medium'
            WHEN unitprice >= 40.00 THEN 'high'
            ELSE 'unknown'
      END AS unitprice_desc  
FROM Production.Products;

--IS NULL / IS NOT NULL
SELECT empid, firstname, lastname, country, region, city
FROM HR.Employees
WHERE region <> N'WA'
OR region IS NULL;

-- Predicate Preferences: The NOT operator precedes AND and OR, and AND precedes OR.
SELECT empid, firstname, lastname, country, region, city
FROM HR.Employees
WHERE region <> N'WA' OR region IS NULL AND firstname = 'Sven' OR lastname = 'Suurs';

--Like: Search Options
SELECT empid, firstname, lastname, country, region, city
FROM HR.Employees
WHERE firstname LIKE 'S%' OR lastname = '%S'
      OR firstname LIKE '_D' OR lastname = '__D'
      OR firstname LIKE '[DEF]%'
      OR lastname = '%[0-9]'
      OR lastname = '^%[A]%';

-- ORDER BY in DISTINCT Clause can only be used for attributes in the SELECT list:
SELECT DISTINCT city, country
FROM HR.Employees
WHERE country = N'USA' AND region = N'WA'
ORDER BY city, country;

--Sorting of Nulls: NULLS are displayed first by default.Use ORDER BY DESC to get the NULL rows below
SELECT orderid, shippeddate
FROM Sales.Orders
WHERE custid = 20
ORDER BY shippeddate;

-- TOP: TOP & TOP PERCENT
DECLARE @n AS BIGINT = 5;

SELECT TOP(@n) orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate DESC;

SELECT TOP(@n) PERCENT orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate DESC;

--Deterministic TOP:
SELECT TOP (3) orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY (SELECT NULL);

--TOP WITH TIES (Fetches more records if duplicates) - Following can return 4/5 or more records
SELECT TOP (3) WITH TIES orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate DESC;

--Filtering Data with OFFSET-FETCH:
-- FIRST/NEXT are same, ROWS /ROW are same
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate DESC, orderid DESC
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY (SELECT NULL)
OFFSET 0 ROWS FETCH FIRST 3 ROWS ONLY;

--JOINS:
--CROSS JOIN
SELECT D.n AS [day], S.n AS [shift]
FROM DBO.nums AS D
      CROSS JOIN DBO.nums AS S
WHERE D.n <= 7
      AND S.n <= 3
ORDER BY [day], [shift];

--INNER JOIN
SELECT
S.companyname AS supplier, S.country,
P.productid, P.productname, P.unitprice
FROM Production.Suppliers AS S
      INNER JOIN Production.Products AS P
            ON S.supplierid = P.supplierid
WHERE S.country = N'Japan';

-- ON & WHERE are the same in INNER JOIN
SELECT
S.companyname AS supplier, S.country,
P.productid, P.productname, P.unitprice
FROM Production.Suppliers AS S
      INNER JOIN Production.Products AS P
            ON S.supplierid = P.supplierid
            AND S.country = N'Japan';
           
-- Self INNER JOIN
SELECT E.empid,
E.firstname + N' ' + E.lastname AS emp,
M.firstname + N' ' + M.lastname AS mgr
FROM HR.Employees AS E
      INNER JOIN HR.Employees AS M
            ON E.mgrid = M.empid;

--OUTER JOIN
--LEFT & RIGHT OUTER JOIN
SELECT
S.companyname AS supplier, S.country,
P.productid, P.productname, P.unitprice
FROM Production.Suppliers AS S
      LEFT OUTER JOIN Production.Products AS P
            ON S.supplierid = P.supplierid
WHERE S.country = N'Japan';

-- Returning even NULL manager from previous query:
SELECT E.empid,
E.firstname + N' ' + E.lastname AS emp,
M.firstname + N' ' + M.lastname AS mgr
FROM HR.Employees AS E
      LEFT OUTER JOIN HR.Employees AS M
            ON E.mgrid = M.empid;

-- Multi-JOIN Query (Left To Right)

-- Multi-JOIN Query: Leaves records with productid = NULL
SELECT S.companyname AS supplier, S.country,
P.productid, P.productname, P.unitprice, C.categoryname
FROM Production.Suppliers AS S
      LEFT OUTER JOIN Production.Products AS P
            ON S.supplierid = P.supplierid
            INNER JOIN Production.Categories AS C
                  ON C.categoryid = P.categoryid
WHERE S.country = N'Japan';

-- Multi-JOIN Query: Counts records with productid = NULL
SELECT S.companyname AS supplier, S.country,
P.productid, P.productname, P.unitprice, C.categoryname
FROM Production.Suppliers AS S
      LEFT OUTER JOIN
            (Production.Products AS P
                  INNER JOIN Production.Categories AS C
                        ON C.categoryid = P.categoryid)
            ON S.supplierid = P.supplierid
WHERE S.country = N'Japan';

-- Subqueries:
-- Self-Contained Subqueries
SELECT productid, productname, unitprice
FROM Production.Products
WHERE unitprice =
      (SELECT MIN(unitprice)FROM Production.Products);

SELECT productid, productname, unitprice
FROM Production.Products
WHERE supplierid IN
      (SELECT supplierid FROM Production.Suppliers
      WHERE country = N'Japan');

-- Correlated Subqueries (To find minimum unit price/category
SELECT categoryid, productid, productname, unitprice
FROM Production.Products AS P1
WHERE unitprice =
      (SELECT MIN(unitprice)
      FROM Production.Products AS P2
      WHERE P2.categoryid = P1.categoryid);

-- Customers who placed orders on February 12, 2007.
SELECT custid, companyname
FROM Sales.Customers AS C
WHERE EXISTS
      (SELECT *
      FROM Sales.Orders AS O
      WHERE O.custid = C.custid
            AND O.orderdate = '20070212');

-- Customers who did not place orders on February 12, 2007.
SELECT custid, companyname
FROM Sales.Customers AS C
WHERE NOT EXISTS
      (SELECT *
      FROM Sales.Orders AS O
      WHERE O.custid = C.custid
            AND O.orderdate = '20070212');
                 
--Top 5 freights for each shipcountry (Correlated Sub query)
SELECT E.shipcountry, E.freight
FROM Sales.Orders AS E
WHERE E.freight IN(
      SELECT TOP 5 F.freight
      FROM Sales.Orders AS F
      WHERE F.shipcountry = E.shipcountry
      ORDER BY F.freight DESC)
ORDER BY E.shipcountry, E.freight DESC;  

-- Table Expressions

-- Derived Tables: Top 5 freights for each shipcountry
SELECT shipcountry, freight
FROM
      (SELECT E.shipcountry, E.freight,
            ROW_NUMBER() OVER(PARTITION BY E.shipcountry
            ORDER BY E.freight DESC) AS rownum
      FROM Sales.Orders AS E) AS D
WHERE rownum <=5;

-- Common table expression (CTE):
WITH D AS
      (SELECT E.shipcountry, E.freight,
            ROW_NUMBER() OVER(PARTITION BY E.shipcountry
            ORDER BY E.freight DESC) AS rownum
      FROM Sales.Orders AS E)

SELECT shipcountry, freight
FROM D
WHERE rownum <=5;

-- CTEs also have a recursive form
WITH EmpsCTE AS
      (SELECT empid, mgrid, firstname, lastname, 0 AS distance
      FROM HR.Employees
      WHERE empid = 9
     
      UNION ALL

      SELECT M.empid, M.mgrid, M.firstname, M.lastname, S.distance + 1 AS distance
      FROM EmpsCTE AS S
            JOIN HR.Employees AS M
                  ON S.mgrid = M.empid)
                 
SELECT empid, mgrid, firstname, lastname, distance
FROM EmpsCTE;

-- Views
IF OBJECT_ID('Sales.TopFreight','V') IS NOT NULL
      DROP VIEW Sales.TopFreight;
GO

CREATE VIEW Sales.TopFreight
AS
      SELECT E.shipcountry, E.freight,
            ROW_NUMBER() OVER(PARTITION BY E.shipcountry
            ORDER BY E.freight DESC) AS rownum
      FROM Sales.Orders AS E;
GO

SELECT shipcountry, freight
FROM Sales.TopFreight
WHERE rownum <=5;

-- IF (Inline Table-Valued Functions)
IF OBJECT_ID('Sales.TopFreightIF','IF') IS NOT NULL
DROP FUNCTION Sales.TopFreightIF;
GO

CREATE FUNCTION Sales.TopFreightIF(@num AS INT) RETURNS TABLE
AS
      RETURN
            WITH D AS
                  (SELECT E.shipcountry, E.freight,
                        ROW_NUMBER() OVER(PARTITION BY E.shipcountry
                        ORDER BY E.freight DESC) AS rownum
                  FROM Sales.Orders AS E)

            SELECT shipcountry, freight
            FROM D
            WHERE rownum <= @num;
GO

SELECT shipcountry, freight
FROM Sales.TopFreightIF(5);


-- Set Operators:
-- Set operators have precedence: INTERSECT precedes UNION and EXCEPT, and UNION and EXCEPT are considered equal.

-- UNION
SELECT country, region, city
FROM HR.Employees

UNION

SELECT country, region, city
FROM Sales.Customers;


-- UNION ALL     
SELECT country, region, city
FROM HR.Employees

UNION ALL

SELECT country, region, city
FROM Sales.Customers;

-- INTERSECT
SELECT country, region, city
FROM HR.Employees

INTERSECT

SELECT country, region, city
FROM Sales.Customers;

-- EXCEPT (Returns only A minus common in A & B)
SELECT country, region, city
FROM HR.Employees

EXCEPT

SELECT country, region, city
FROM Sales.Customers;

--GROUP BY
SELECT shipperid, YEAR(shippeddate) AS shippedyear,
      COUNT(*) AS numorders
FROM Sales.Orders
WHERE shippeddate IS NOT NULL
GROUP BY shipperid, YEAR(shippeddate)
HAVING COUNT(*) < 100;

-- COUNT(shippeddate) ignores NULL
SELECT shipperid,
      COUNT(*) AS numorders,
      COUNT(shippeddate) AS shippedorders,
      MIN(shippeddate) AS firstshipdate,
      MAX(shippeddate) AS lastshipdate,
      SUM(val) AS totalvalue
FROM Sales.OrderValues
GROUP BY shipperid;

  
--GROUP BY with DISTINCT
SELECT shipperid, COUNT(DISTINCT shippeddate) AS numshippingdates
FROM Sales.Orders
GROUP BY shipperid;

-- Workaround is to apply an aggregate function like MAX to the column, for GROUP BY
SELECT S.shipperid,
      MAX(S.companyname) AS numorders,
      COUNT(*) AS shippedorders
FROM Sales.Shippers AS S
      INNER JOIN Sales.Orders AS O
            ON S.shipperid = O.shipperid
GROUP BY S.shipperid;


---------------------------------------------------------------------
-- Section A - for on-premises SQL Server only
---------------------------------------------------------------------
-- 1. Connect to SQL Azure, master database
USE master; -- used only as a test; will fail if not connected to master

-- 2. Run following if TSQL2012 database already exists, otherwise skip
IF DB_ID('TSQL2012') IS NOT NULL DROP DATABASE TSQL2012;
GO

-- 3. Run the following code to create an empty database called TSQL2012
CREATE DATABASE TSQL2012;
GO

-- 4. Connect to TSQL2012 before running the rest of the code
USE TSQL2012; -- used only as a test; will fail if not connected to TSQL2012
GO

---------------------------------------------------------------------
-- Create Schemas
---------------------------------------------------------------------

CREATE SCHEMA HR AUTHORIZATION dbo;
GO

---------------------------------------------------------------------
-- Create Tables
---------------------------------------------------------------------
-- Create table HR.Employees

CREATE TABLE HR.Employees
(
  empid           INT          NOT NULL IDENTITY,
  lastname        NVARCHAR(20) NOT NULL,
  firstname       NVARCHAR(10) NOT NULL,
  title           NVARCHAR(30) NOT NULL,
  titleofcourtesy NVARCHAR(25) NOT NULL,
  birthdate       DATETIME     NOT NULL,
  hiredate        DATETIME     NOT NULL,
  address         NVARCHAR(60) NOT NULL,
  city            NVARCHAR(15) NOT NULL,
  region          NVARCHAR(15) NULL,
  postalcode      NVARCHAR(10) NULL,
  country         NVARCHAR(15) NOT NULL,
  phone           NVARCHAR(24) NOT NULL,
  mgrid           INT          NULL,
  CONSTRAINT PK_Employees PRIMARY KEY(empid),
  CONSTRAINT FK_Employees_Employees FOREIGN KEY(mgrid)
    REFERENCES HR.Employees(empid),
  CONSTRAINT CHK_birthdate CHECK(birthdate <= CURRENT_TIMESTAMP)
);

CREATE NONCLUSTERED INDEX idx_nc_lastname   ON HR.Employees(lastname);
CREATE NONCLUSTERED INDEX idx_nc_postalcode ON HR.Employees(postalcode);


---------------------------------------------------------------------
-- Populate Tables
---------------------------------------------------------------------

SET NOCOUNT ON;

-- Populate table HR.Employees
SET IDENTITY_INSERT HR.Employees ON;
INSERT INTO HR.Employees(empid, lastname, firstname, title, titleofcourtesy, birthdate, hiredate, address, city, region, postalcode, country, phone, mgrid)
  VALUES(1, N'Davis', N'Sara', N'CEO', N'Ms.', '19581208 00:00:00.000', '20020501 00:00:00.000', N'7890 - 20th Ave. E., Apt. 2A', N'Seattle', N'WA', N'10003', N'USA', N'(206) 555-0101', NULL);
INSERT INTO HR.Employees(empid, lastname, firstname, title, titleofcourtesy, birthdate, hiredate, address, city, region, postalcode, country, phone, mgrid)
  VALUES(2, N'Funk', N'Don', N'Vice President, Sales', N'Dr.', '19620219 00:00:00.000', '20020814 00:00:00.000', N'9012 W. Capital Way', N'Tacoma', N'WA', N'10001', N'USA', N'(206) 555-0100', 1);
SET IDENTITY_INSERT HR.Employees OFF;

-- This INSERT will also work:
SET IDENTITY_INSERT HR.Employees ON;
INSERT INTO HR.Employees(empid, lastname, firstname, title, titleofcourtesy, birthdate, hiredate, address, city, region, postalcode, country, phone, mgrid)VALUES
  (1, N'Davis', N'Sara', N'CEO', N'Ms.', '19581208 00:00:00.000', '20020501 00:00:00.000', N'7890 - 20th Ave. E., Apt. 2A', N'Seattle', N'WA', N'10003', N'USA', N'(206) 555-0101', NULL),
  (2, N'Funk', N'Don', N'Vice President, Sales', N'Dr.', '19620219 00:00:00.000', '20020814 00:00:00.000', N'9012 W. Capital Way', N'Tacoma', N'WA', N'10001', N'USA', N'(206) 555-0100', 1);
SET IDENTITY_INSERT HR.Employees OFF;

-- This INSERT will also work:
SET IDENTITY_INSERT HR.Employees1 ON;
INSERT INTO HR.Employees1(empid, lastname, firstname, title, titleofcourtesy, birthdate, hiredate, address, city, region, postalcode, country, phone, mgrid)
      SELECT empid, lastname, firstname, title, titleofcourtesy, birthdate, hiredate, address, city, region, postalcode, country, phone, mgrid
      FROM HR.Employees;
SET IDENTITY_INSERT HR.Employees OFF;

-- Update:
UPDATE HR.Employees1
SET birthdate += DAY(300)
WHERE lastname = 'Davis';

SET NOCOUNT OFF;
GO

-- TRUNCATE:
TRUNCATE TABLE HR.Employees1;

-- DELETE:
DELETE FROM HR.Employees1;

DELETE FROM HR.Employees1
WHERE lastname = 'Davis';

/*The DELETE and TRUNCATE statements have a number of important differences between
them:
1. The TRUNCATE statement deletes all rows from the target table. Unlike the DELETE statement,
it doesn’t have an optional filter, so it’s all or nothing.

2. The DELETE statement writes significantly more to the transaction log compared to the
TRUNCATE statement. For DELETE, SQL Server records in the log the actual data that
was deleted. For TRUNCATE, SQL Server records information only about which pages
were deallocated. As a result, the TRUNCATE statement tends to be substantially faster.

3.The DELETE statement doesn’t attempt to reset an identity property if one is associated
with a column in the target table. The TRUNCATE statement does. If you use
TRUNCATE and would prefer not to reset the property, you need to store the current
identity value plus one in a variable (using the IDENT_CURRENT function), and reseed
the property with the stored value after the truncation.

4.The DELETE statement is supported if there’s a foreign key pointing to the table in
question as long as there are no related rows in the referencing table. TRUNCATE is not
allowed if a foreign key is pointing to the table—even if there are no related rows in
the referencing table, and even if the foreign key is disabled.

5.The DELETE statement is allowed against a table involved in an indexed view. A TRUNCATE
statement is disallowed in such a case.

6.The DELETE statement requires DELETE permissions on the target table. The TRUNCATE
statement requires ALTER permissions on the target table.*/

--DROP Table/Database
DROP TABLE HR.Employees1;
DROP DATABASE TSQL2012;

-- ALTER
ALTER TABLE HR.Employees
ADD DateOfBirth DATE;

ALTER TABLE HR.Employees
ALTER COLUMN DateOfBirth DATETIME;

ALTER TABLE HR.Employees

DROP COLUMN DateOfBirth;

No comments:

Post a Comment