Check how many records in a data file - sql-server

I need to know how many records stored in single data file after carrying out table partitioning. Here I'm using Sales.SalesOrderDetail from AdventureWorks2016 sample database for demonstration.
I want TSQL query/script displaying the following output:
FileName Count
file1 27405
file2 56573
file3 37339
Here is the procedure I wrote:
Creating a database
CREATE DATABASE PTDB
Creating file group
ALTER DATABASE PTDB
ADD FILEGROUP MyFileGroup
Creating 4 files to store the split table records (I'm using X instead of repeating the code 4 times):
ALTER DATABASE PTDB
ADD FILE
(
NAME = 'fileX',
FILENAME = 'D:\SQLServer2017Media\MSSQLSERVER\MSSQL14.MSSQLSERVER\TablePt\ptX.ndf',
SIZE = 120,
FILEGROWTH = 10
)
TO FILEGROUP MyFileGroup
Creating partition function and partition scheme:
CREATE PARTITION FUNCTION fnOrderYears(DateTime)
AS RANGE LEFT FOR VALUES
('2012-12-31','2013-12-31','2014-12-31')
GO
CREATE PARTITION SCHEME psOrderYearsFiles
AS PARTITION fnOrderYears ALL TO (MyFileGroup)
GO
Creating the partitioned table and load the data from AdventureWorks2016.Sales.SalesOrderDetail table:
CREATE TABLE [dbo].[SalesOrderDetail] (
[SalesOrderID] [int] NOT NULL,
[SalesOrderDetailID] [int]NOT NULL,
CONSTRAINT SalesOrderDetailPK PRIMARY KEY
NONCLUSTERED(SalesOrderDetailID),
[CarrierTrackingNumber] [nvarchar](25) NULL,
[OrderQty] [smallint] NOT NULL,
[ProductID] [int] NOT NULL,
[SpecialOfferID] [int] NOT NULL,
[UnitPrice] [money] NOT NULL,
[UnitPriceDiscount] [money] NOT NULL,
[LineTotal] money,
[OrderDate] [datetime] NOT NULL
)
GO
INSERT INTO [dbo].[SalesOrderDetail]
(
SalesOrderID, SalesOrderDetailID, CarrierTrackingNumber,
OrderQty, ProductID, SpecialOfferID, UnitPrice, UnitPriceDiscount,
LineTotal, OrderDate
)
SELECT
SalesOrderID, SalesOrderDetailID, CarrierTrackingNumber,
OrderQty, ProductID, SpecialOfferID, UnitPrice, UnitPriceDiscount,
LineTotal, ModifiedDate
FROM AdventureWorks2016.Sales.SalesOrderDetail
BTW, I'm using this SQL query but I'm not sure if it is right:
SELECT $PARTITION.fnOrderYears(OrderDate) AS Partition,
COUNT(*) AS Count
FROM SalesOrderDetail
GROUP BY $PARTITION.fnOrderYears(OrderDate)
ORDER BY Partition;

Related

After insert trigger doesn't work when using Inserted

I'm trying to write a trigger on my Employees table that should not allow the insertion of a new employee that has a hire date that is older than the hire date of his boss
CREATE TABLE [dbo].[Employees]
(
[EID] [int] IDENTITY(1,1) NOT NULL,
[Ename] [nvarchar](20) NOT NULL,
[Gender] [nvarchar](1) NOT NULL,
[IsMarried] [nvarchar](1) NOT NULL,
[Birthdate] [date] NOT NULL,
[HireDate] [date] NOT NULL,
[Salary] [float] NOT NULL,
[Notes] [nvarchar](200) NULL,
[NationalityID] [int] NULL,
[BossID] [int] NULL,
CONSTRAINT [PK_Employees]
PRIMARY KEY CLUSTERED ()
)
And here's the trigger code:
CREATE TRIGGER [dbo].[Trig_04]
ON [dbo].[Employees]
AFTER INSERT
AS
BEGIN
IF ((SELECT INSERTED.HireDate FROM INSERTED WHERE BossID <> EID) <
(SELECT Employees.HireDate FROM Employees
WHERE EID IN (SELECT Employees.BossID FROM Employees WHERE BossID <> EID)))
ROLLBACK
END
It executes normally (no errors) but it just doesn't work, but when I was using the employees table in the subquery instead of the inserted table, it was working normally. Does anyone have an answer for this?
You have to write triggers in SQL Server to handle the fact that INSERTED could contain multiple records. You cannot assume it will only be a single record. I think the following is what you are looking for:
if exists (
select 1
from Inserted I
where I.BossID <> I.EID
and I.HireDate < (select E.HireDate from Employees E where E.EID = I.BossID)
) begin
ROLLBACK;
end

Compare two child table items with two similar parent tables from two databases

I need help creating a query to compare the Equipment in one database to Asset in another table. Here is my database setup:
CREATE DATABASE database1
GO
USE [database1]
GO
CREATE TABLE [dbo].[Application]
(
[ApplicationID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[APP_NUMBRER] [int] NULL,
)
GO
CREATE TABLE [dbo].[Equipment]
(
[EquipID] [int] IDENTITY(1,1) NOT NULL,
[ApplicationID] [int] NULL,
[Year] [varchar](4) NULL,
[Make] [varchar](50) NULL,
[Model] [varchar](50) NULL
)
ALTER TABLE [dbo].[Equipment] WITH CHECK
ADD CONSTRAINT [FK_EQUIP_1]
FOREIGN KEY([ApplicationID]) REFERENCES [dbo].[APPLICATION] ([ApplicationID])
GO
INSERT INTO [Application]
VALUES (1), (2), (3)
INSERT INTO [Equipment]
VALUES (1, '1998', 'Equip1', 'Model1'),
(1, '1855', 'Equip2', 'Model2'),
(2, '1222', 'Equip3', 'Model4'),
(2, '1333', 'Equip4', 'Model4'),
(3, '1777', 'Equip5', 'Model5')
GO
CREATE DATABASE database2
GO
USE [database2]
GO
CREATE TABLE [dbo].[Application]
(
[APP_KEY] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[APP_DESCRIPTION] [varchar](40) NOT NULL
)
GO
CREATE TABLE [dbo].[ASSET]
(
[AS_KEY] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[AS_APP_FKEY] [int] NOT NULL,
[Year] [varchar](4) NULL,
[Make] [varchar](50) NULL,
[Model] [varchar](50) NULL
)
ALTER TABLE [dbo].[ASSET] WITH CHECK
ADD CONSTRAINT [FK_ASSET_1]
FOREIGN KEY([AS_APP_FKEY]) REFERENCES [dbo].[APPLICATION] ([APP_KEY])
GO
INSERT INTO [Application]
VALUES ('AppDesc1'), ('AppDesc2')
INSERT INTO [ASSET]
VALUES (1, '1998', 'Asset1', 'Db2Model1'),
(1, '1855', 'Asset2', 'Db2Model2'),
(2, '1222', 'Asset3', 'Db2Model3'),
(2, '1333', 'Asset4', 'Db2Model4')
GO
My query:
SELECT
ap1.APP_NUMBRER,
e.Year, e.Make, e.Model,
db2.APP_KEY, db2.Year, db2.Make, db2.Model
FROM
database1.dbo.Application ap1
JOIN
database1.dbo.Equipment e ON E.APPLICATIONID = ap1.APPLICATIONID
LEFT JOIN
(SELECT
APP_KEY, Year, Make, Model
FROM
[database2].dbo.APPLICATION ap2
JOIN
[database2].dbo.ASSET ON asset.AS_APP_FKEY = ap2.APP_KEY) db2 ON ap1.APP_NUMBRER = db2.APP_KEY
Result:
Expected result: my query is creating a few duplicate items that compare all of db1 equipment to db2 assets. I want a one to one comparison. I don't want items 2,3,6,7. Is this because of how the table relationships are set up.
It's because you're only joining on APP_NUMBER=APP_KEY. Add Year=Year to the JOIN and you will get your desired result.
Add YEAR in you join. It will solve your problem.
SELECT ap1.APP_NUMBRER, e.Year, e.Make, e.Model, db2.APP_KEY, db2.Year, db2.Make, db2.Model
FROM database1.dbo.Application ap1
JOIN database1.dbo.Equipment e
ON E.APPLICATIONID = ap1.APPLICATIONID
LEFT JOIN(
SELECT APP_KEY, Year, Make, Model
FROM [database2].dbo.APPLICATION ap2
JOIN [database2].dbo.ASSET
ON asset.AS_APP_FKEY = ap2.APP_KEY
) db2
ON ap1.APP_NUMBRER = db2.APP_KEY AND e.Year = db2.Year
Amend your JOIN condition like so
db2 ON ap1.APP_NUMBRER = db2.APP_KEY
AND e.Year = db2.Year

How can I use unpivot for a two table join with SQL Server?

I have two tables:
CREATE TABLE [dbo].[Phrase] (
[PhraseId] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
[English] NVARCHAR (250) NOT NULL,
[CreatedBy] INT DEFAULT ((1)) NOT NULL,
[ModifiedBy] INT DEFAULT ((1)) NOT NULL,
PRIMARY KEY CLUSTERED ([PhraseId] ASC)
);
CREATE TABLE [dbo].[AspNetUsers] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[FirstName] NVARCHAR (MAX) NULL,
[LastName] NVARCHAR (MAX) NULL,
CONSTRAINT [PK_dbo.AspNetUsers] PRIMARY KEY CLUSTERED ([Id] ASC)
);
The CreatedBy and the ModifiedBy in the Phrase table are both joined to the Id in the AspNetUsers table.
What I would like to do is to create a report that uses Pivot and links them so it looks like this:
LastName CreatedByQty ModifiedQty
ad1 100 50
ad2 10 5
ad3 200 99
I have used unpivot for a single table but never for a two table join like this. Would appreciate some advice that would help me to get the report like one above.
Serge's answer - I tried this but it gives a completely wrong output :-( Adding the output here and will add a comment in the suggested answer:
1024 420172 420172
1025 0 0
1 360328 360328
2 1331368 1331368
3 29574 29574
1023 484800 484800
Use two LEFT JOIN's and COUNT:
SELECT u.LastName
,COALESCE(COUNT(DISTINCT pc.PhraseId),0) AS CreatedByQty
,COALESCE(COUNT(DISTINCT pm.PhraseId),0) AS ModifiedByQty
FROM dbo.AspNetUsers u
LEFT JOIN dbo.Phrase pc
ON u.Id = pc.CreatedBy
LEFT JOIN dbo.Phrase pm
ON u.Id = pm.ModifiedBy
GROUP BY u.LastName
Use COALESCE to return 0 instead of NULL if a user did not create/modify any phrases.

SQL Server: How to improve performance for queries with multiple CTEs and subqueries in the WHERE clause

I have the following two tables:
CREATE TABLE Portfolio.DailyPortfolio
(
BbgID varchar(30) NOT NULL,
Ticker varchar(22) NULL,
Cusip char(9) NULL,
SecurityDescription varchar(50) NOT NULL,
AssetCategory varchar(25) NOT NULL,
LSPosition char(3) NULL,
Ccy varchar(25) NOT NULL,
Quantity int NULL,
AvgCost decimal(7,3) NULL,
PriceLocal decimal(7,3) NULL,
Cost int NULL,
MktValNet int NULL,
GLPeriod int NULL,
Beta decimal(4,2) NULL,
BetaExpNet int NULL,
BetaExpGross int NULL,
Delta decimal(4,2) NULL,
DeltaExpNet int NULL,
DeltaExpGross int NULL,
Issuer varchar(48) NOT NULL,
Country varchar(30) NOT NULL,
Region varchar(20) NOT NULL,
Sector varchar(30) NOT NULL,
Industry varchar(48) NOT NULL,
MktCapCategory varchar(24) NULL,
MktCapEnd int NULL,
Date date NOT NULL,
PortfolioID AS BbgID+LSPosition+ Convert(varchar(8),Date,112) Persisted Primary Key
)
GO
Here is the second table:
CREATE TABLE Portfolio.DailyStats
(
Date date NOT NULL Primary Key,
NAV int NOT NULL,
SP500 decimal(8,4) NULL,
R2K decimal(8,4) NULL,
NetExp decimal(8,4) NULL,
GrossExp decimal(8,4) NULL,
)
GO
ALTER TABLE Portfolio.DailyStats
ADD [YrMn] as CONVERT(varchar(7), Date)
GO
Between 80-100 rows get added to the DailyPortfolio table every business day (the table has about 32,000 rows currently). 1 row gets added to the DailyStats table every business day (it has about 500 rows currently). The Date column in the Daily Portfolio table has a Foreign Key relationship with the Date column in the DailyStats table.
I had to create a view that included a few columns from both tables using last quarter as the date range. The last column of this view uses the Average of the NAV column in its calculation where the Average is calculated by using the NAV on the 1st date of each of the 3 months in the quarter. Here is my DDL for the view:
CREATE VIEW Portfolio.PNLLastQTD
AS
WITH CTE1
AS
(
Select Date, NAV,YrMn, ROW_NUMBER() OVER (PARTITION BY YrMn ORDER BY Date) AS Row
FROM Portfolio.DailyStats
WHERE DATE BETWEEN
(SELECT Convert(date, DATEADD(q, DATEDIFF(q,0,GETDATE()) -1 ,0)))
AND
(SELECT Convert(date, DATEADD(s,-1,DATEADD(q, DATEDIFF(q,0,GETDATE()),0))))
),
CTE2
AS
(
SELECT AvG (NAV) As AvgNAV
FROM CTE1
WHERE Row=1
)
SELECT IssuerLS, Issuer, Ticker, SUM (GLPeriod) As [PNL],
CAST(SUM(GLPeriod)As Decimal (13,2)) / CAST(CTE2.[AvgNAV] As Decimal (13,2)) as [%ofNAV]
FROM Portfolio.DailyPortfolioIssuerLS ls
JOIN cte2 on 1=1
WHERE ReportDate
BETWEEN
(SELECT Convert(date, DATEADD(q, DATEDIFF(q,0,GETDATE()) -1 ,0)))
AND
(SELECT Convert(date, DATEADD(s,-1,DATEADD(q, DATEDIFF(q,0,GETDATE()),0))))
GROUP BY
Issuer, Ticker, IssuerLS, CTE2.[AvgNAV]
GO
The view works fine but takes almost 20 seconds to execute! I have a couple of questions here:
Should there be some changes made to my DDL for the view?
Is it a good idea to create a non-clustered index on the date column (if at all possible) of the DailyPortfolio table?
Is there anything else I should be thinking of to improve query performance for this particular issue?
Thanks much for your help. Please forgive blatant mistakes as I'm new to SQL.
I wanted to close on the loop on this question. What I needed to do here was create two non clustered indices. I used the following steps:
Placed my query on the query window.
Clicked on "Display Estimated Execution Plan" button on my toolbox which immediately informed me of a missing non cluster index.
Created the first non-clustered index:
USE [OurDB]
GO
CREATE NONCLUSTERED INDEX NCI_DailyPort_Issuer_Date
ON [Portfolio].[DailyPortfolio] ([Issuer],[Date])
GO
Repeated step 2 and created the second non-clustered index as recommended:
USE [OurDB]
GO
CREATE NONCLUSTERED INDEX NCI_DailyPort_Date_INC_DexpN_Issuer
ON [Portfolio].[DailyPortfolio] ([Date])
INCLUDE ([DeltaExpNet],[Issuer])
GO
The query now takes less than 3 seconds to execute, significantly better than the 24 seconds it was taking before this.
Note: If you right click on the line that informs you about the missing index, you can choose an option to see the code for the index which saves you time.

T-SQL ISNULL() incorrect behaviour with reserved word 'Primary' (SQL Server 2014)

I came across this the other day, and it wasn't a major as I could work around it, but wondered if anyone could offer any insight, or have I found an obscure bug?
We have an old database table that has a column called Primary, which is obviously a reserved word. When running a T-SQL query I came across some weird results which seems to suggest that the ISNULL function is treating the column name as if it were the reserved word. Is it just because it's late on Friday and I'm missing something really obvious here, or is something weird going on here?
Here is some simple SQL to set up a couple of test tables to illustrate it.
CREATE TABLE [dbo].[Customer]
(
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Customer]
PRIMARY KEY CLUSTERED ([CustomerID] ASC)
) ON [PRIMARY]
CREATE TABLE [dbo].[OrderTaken]
(
[OrderID] [int] IDENTITY(1,1) NOT NULL,
[Description] [varchar](50) NOT NULL,
[CustomerID] [int] NOT NULL,
[Primary] [bit] NOT NULL,
CONSTRAINT [PK_Order]
PRIMARY KEY CLUSTERED ([OrderID] ASC)
) ON [PRIMARY]
ALTER TABLE [dbo].[OrderTaken] WITH CHECK
ADD CONSTRAINT [FK_Order_Customer]
FOREIGN KEY([CustomerID]) REFERENCES [dbo].[Customer] ([CustomerID])
GO
ALTER TABLE [dbo].[OrderTaken] CHECK CONSTRAINT [FK_Order_Customer]
GO
INSERT INTO [dbo].[Customer](Name) VALUES('Bob')
INSERT INTO [dbo].[Customer](Name) VALUES('Dave')
INSERT INTO [dbo].[Customer](Name) VALUES('Fred')
INSERT INTO [dbo].[Customer](Name) VALUES('Paul')
GO
INSERT INTO [dbo].[OrderTaken](Description, CustomerID, [Primary])
VALUES ('Order1', 1, 1)
INSERT INTO [dbo].[OrderTaken](Description, CustomerID, [Primary])
VALUES('Order2', 2, 1)
INSERT INTO [dbo].[OrderTaken](Description, CustomerID, [Primary])
VALUES('Order3', 2, 1)
INSERT INTO [dbo].[OrderTaken](Description, CustomerID, [Primary])
VALUES('Order4', 3, 0)
INSERT INTO [dbo].[OrderTaken](Description, CustomerID, [Primary])
VALUES('Order5', 3, 0)
Go
and here is the query
SELECT
C.CustomerID, C.Name,
O.OrderID, ISNULL(O.[OrderID], -1),
O.[Primary], ISNULL(O.[Primary], -1) as Weird
FROM
Customer C
LEFT JOIN
OrderTaken O ON C.CustomerID = O.CustomerID
Notice that for the customer row that doesn't have an Order, O.Primary is NULL , but ISNULL(O.[Primary],-1) returns 1 and not -1
The problem is not in field name, it's in field type - bit.
It cant hold -1 value, only 0 and 1. And when SQL Server tries to change value to -1 it checks that -1 <> 0 and sets 1 (true). See documentation.
So convert the field before checking:
SELECT
C.CustomerID,
C.Name,
O.OrderID,
ISNULL(O.[OrderID],-1),
O.[Primary],
ISNULL(CONVERT(int,O.[Primary]),-1) as Weird
FROM Customer C
LEFT JOIN OrderTaken O
ON C.CustomerID = O.CustomerID
Or change column type to int or tinyint.

Resources