How to fetch the json column data from database in SQL Server? - sql-server

I have created a small demo app using .NET 6 with EF core and I am using Serilog to log exceptions into my database.
Everything is working fine but now I want to display the log table details on my view (cshtml).
Current log table columns are:
Message
MessageTemplate
Level
TimeStamp
Exception
LogEvent
ClientIP
I have created a stored procedure to fetch the log details but the LogEvent column contains json data like below:
{
"TimeStamp":"2022-07-23T20:00:55.6987337",
"Level":"Information",
"Message":"Started executing Privacy",
"MessageTemplate":"Started executing Privacy",
"Properties":{
"ActionId":"ba7d94ab-3758-4a4c-a3ef-2bda514a531c",
"ActionName":"Serilog_Demo.Controllers.HomeController.Privacy (Serilog_Demo)",
"RequestId":"SomeRequest",
"RequestPath":"/Home/Privacy",
"ConnectionId":"something",
"MachineName":"Test"
}
}
My goal is to get "Message","TimeStamp","ClientIP", ActionName","MachinName","RequestPath" from above json object. How do I do that? I followed this link
Which shows how to get the JSON data but it seems like I am making some mistake in fetching the records.
Here is my stored procedure:
CREATE PROCEDURE uspGetApplicationLogs
AS
BEGIN
SELECT ClientIP, LogEvent
FROM ApplicationLogs
ORDER BY TimeStamp DESC;
-- this code is not working for me
DECLARE #json NVARCHAR(MAX);
SET #json = (SELECT LogEvent FROM ApplicationLogs)
SELECT *
FROM OPENJSON((SELECT TOP 1 LogEvent FROM ApplicationLogs))
WITH (logDateTime timestamp '$.TimeStamp',
level nvarchar(255) '$.Level',
ActionName NVARCHAR(MAX) '$.Properties.ActionName');
END
And here is my table script, in case if anybody needs it.
CREATE TABLE [dbo].[ApplicationLogs]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Message] [nvarchar](max) NULL,
[MessageTemplate] [nvarchar](max) NULL,
[Level] [nvarchar](max) NULL,
[TimeStamp] [datetime] NULL,
[Exception] [nvarchar](max) NULL,
[LogEvent] [nvarchar](max) NULL,
[ClientIP] [nvarchar](max) NULL,
[UserName] [nvarchar](max) NULL,
[ClientAgent] [nvarchar](max) NULL
)
SET IDENTITY_INSERT [dbo].[ApplicationLogs] ON
GO
INSERT INTO [dbo].[ApplicationLogs] ([Id], [Message], [MessageTemplate], [Level], [TimeStamp], [Exception], [LogEvent], [ClientIP], [UserName], [ClientAgent])
VALUES (1, N'Started executing Privacy', N'Started executing Privacy', N'Information', CAST(N'2022-07-23T20:00:55.700' AS DateTime), NULL, N'{"TimeStamp":"2022-07-23T20:00:55.6987337","Level":"Information","Message":"Started executing Privacy","MessageTemplate":"Started executing Privacy","Properties":{"ActionId":"ba7d94ab-3758-4a4c-a3ef-2bda514a531c","ActionName":"Serilog_Demo.Controllers.HomeController.Privacy (Serilog_Demo)","RequestId":"0E:00000004","RequestPath":"/Home/Privacy","ConnectionId":"SomeConnection","MachineName":"Test"}}', NULL, NULL, NULL)
GO
INSERT INTO [dbo].[ApplicationLogs] ([Id], [Message], [MessageTemplate], [Level], [TimeStamp], [Exception], [LogEvent], [ClientIP], [UserName], [ClientAgent])
VALUES (2, N'Attempted to divide by zero.', N'Attempted to divide by zero.', N'Error', CAST(N'2022-07-23T20:00:55.803' AS DateTime), N'System.DivideByZeroException: Attempted to divide by zero.
at Serilog_Demo.Controllers.HomeController.Privacy() in \Sol_Serilog_Demo\Serilog_Demo\Controllers\HomeController.cs:line 24', N'{"TimeStamp":"2022-07-23T20:00:55.8034293","Level":"Error","Message":"Attempted to divide by zero.","MessageTemplate":"Attempted to divide by zero.","Exception":"System.DivideByZeroException: Attempted to divide by zero.\r\n at Serilog_Demo.Controllers.HomeController.Privacy() in \Sol_Serilog_Demo\\Serilog_Demo\\Controllers\\HomeController.cs:line 24","Properties":{"ActionId":"ba7d94ab-3758-4a4c-a3ef-2bda514a531c","ActionName":"Serilog_Demo.Controllers.HomeController.Privacy (Serilog_Demo)","RequestId":"4","RequestPath":"/Home/Privacy","ConnectionId":"VIB38TE","MachineName":"Test"}}', NULL, NULL, NULL)
GO
SET IDENTITY_INSERT [dbo].[ApplicationLogs] OFF
GO
I do not want to deserialize the LogEvent column data at .net end. Can anybody help to parse the JSON and get the log event value from my database?

You need to use CROSS APPLY to feed the JSON column into OPENJSON
CREATE PROCEDURE uspGetApplicationLogs
AS
SET NOCOUNT ON;
SELECT
al.Message,
al.ClientIP,
al.LogEvent,
al.TimeStamp,
j.*
FROM ApplicationLogs al
CROSS APPLY OPENJSON(al.LogEvent)
WITH (
logDateTime datetime2 '$.TimeStamp',
level nvarchar(255) '$.Level',
RequestPath nvarchar(250) '$.Properties.RequestPath',
MachineName nvarchar(250) '$.Properties.MachineName',
ActionName nvarchar(250) '$.Properties.ActionName'
) j
ORDER BY al.TimeStamp DESC;

Related

How to avoid duplicate record while inserting data using user defined table type in SQL Server

I am trying to insert entire model in database using my .net application. I am using a user-defined table type.
This is my procedure and user-defined table; I am using SQL Server 2012.
CREATE TYPE [dbo].[TmpAccessRequest] AS TABLE
(
[RequestId] [int] NULL,
[RequesterID] [int] NULL,
[RequestType] [int] NULL,
[NextApprover] [int] NULL,
[RequestStatus] [varchar](100) NULL,
[Delegation] [int] NULL,
[CreatedOn] [date] NULL,
[CreatedBy] [varchar](100) NULL,
[Description] [varchar](max) NULL,
[IsSepecialRequest] [bit] NULL,
[DelegationDetailID] [int] NULL,
[IsActive] [bit] NULL,
[IsDeleted] [bit] NULL,
[ModifiedOn] [date] NULL
)
GO
CREATE PROCEDURE [dbo].[proc_SaveAccessRequest]
(#TmpAR TmpAccessRequest READONLY,
#IsUAMSRequest BIT,
#RequestID INT OUTPUT)
AS
BEGIN
INSERT INTO tblRequests (RequesterID, RequestType, NextApprover, RequestStatus,
Delegation, CreatedOn, CreatedBy, Description,
IsSepecialRequest, DelegationDetailID, IsActive, IsDeleted, ModifiedOn)
SELECT
RequesterID, RequestType, NextApprover, RequestStatus,
Delegation, CreatedOn, CreatedBy, Description,
IsSepecialRequest, DelegationDetailID, IsActive, IsDeleted, ModifiedOn
FROM
#TmpAR
SET #RequestID = SCOPE_IDENTITY()
--SET #RequestID=IDENT_CURRENT('tblRequests')
SELECT #RequestID
END
I want to check if duplicate data should not insert at the same time. So how can I do that with user-defined table type ?
Please find the changes done to your script to avoid inserting duplicate record. So i considered two columns data should be unique to avoid duplication for user understanding purpose
CREATE PROCEDURE [dbo].[proc_SaveAccessRequest]
(
#TmpAR TmpAccessRequest READONLY,
#IsUAMSRequest bit,
#RequestID int OUTPUT
)
AS
BEGIN
Insert into tblRequests
(
RequesterID
,RequestType
,NextApprover
,RequestStatus
,Delegation
,CreatedOn
,CreatedBy
,[Description]
,IsSepecialRequest
,DelegationDetailID
,IsActive
,IsDeleted
,ModifiedOn
)
SELECT
RequesterID
,RequestType
,NextApprover
,RequestStatus
,Delegation
,CreatedOn
,CreatedBy
,Description
,IsSepecialRequest
,DelegationDetailID
,IsActive
,IsDeleted
,ModifiedOn
FROM #TmpAR
WHERE NOT EXISTS ( SELECT 1
FROM tblRequests i
INNER JOIN #TmpAR o
ON i.RequesterID = o.RequesterID
AND i.RequestType = o.RequestType
AND i.NextApprover = o.NextApprover)
SELECT #RequestID = SCOPE_IDENTITY()
SELECT #RequestID
END

Trigger to update a table with timestamp when data is inserted into another table

I am very new to triggers. need help here.
I have two tables, [dbo].[Demand_Request] and [dbo].[Modified_Demand_Request].
CREATE TABLE [dbo].[Demand_Request]
(
[CASE_ID] [INT] NULL,
[TE_PART_NUMBER] [NVARCHAR](50) NULL,
[VALUE] [FLOAT] NULL,
[DEMAND_DATE] [DATETIME] NULL
)
CREATE TABLE [dbo].[Modified_Demand_Request]
(
[CASE_ID] [INT] NULL,
[TE_PART_NUMBER] [NVARCHAR](50) NULL,
[VALUE] [FLOAT] NULL,
[DEMAND_DATE] [DATETIME] NULL,
[Modified_On] [DATETIME] NULL
)
When data is inserted into [dbo].[Demand_Request], I want to add the same data into table [dbo].[Modified_Demand_Request] along with a timestamp.
Below is my trigger, but it is not working:
CREATE TRIGGER [dbo].[Modified_Demand_Request1]
ON [dbo].[Demand_Request]
AFTER UPDATE
AS
INSERT INTO [dbo].[Modified_Demand_Request] ([CASE_ID], [TE_PART_NUMBER],[VALUE], [DEMAND_DATE], [Modified_On])
SELECT
[CASE_ID], [TE_PART_NUMBER], [VALUE],
[DEMAND_DATE], GETDATE()
FROM
INSERTED
May I know that how you are inserting the data. I tried the same as below (with "FOR INSERT" trigger) and able to get the data in Modified_Demand_Request table.
insert into [Demand_Request] values(1,'Test',1.0,GETDATE())
insert into [Demand_Request] values(2,'Test1',1.0,GETDATE())
insert into [Demand_Request] values(3,'Test2',1.0,GETDATE())
insert into [Demand_Request] values(14,'Test2',1.0,GETDATE())
Try this FOR INSERT
Create Trigger [dbo].[Modified_Demand_Request1]
on [dbo].[Demand_Request]
FOR INSERT
AS
Insert Into[dbo].[Modified_Demand_Request] ([CASE_ID],[TE_PART_NUMBER],[VALUE],[DEMAND_DATE],[Modified_On])
SELECT [CASE_ID],[TE_PART_NUMBER],[VALUE],[DEMAND_DATE],GETDATE()
FROM INSERTED
for DEMO REXTESTER
Extra Reference: Firing Trigger for Bulk Insert

Read data from another columns to current table in SQL

I have these tables :
CREATE TABLE [dbo].[FileISOManagers]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[LineId] [int] NOT NULL,
[Revision] [nvarchar](max) NULL,
[FileName] [nvarchar](max) NULL,
[UserId] [int] NOT NULL,
[SubmitDateTime] [datetime] NOT NULL
)
CREATE TABLE [dbo].[Lines]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[LineNumber] [nvarchar](max) NULL,
[DocumentNumber] [nvarchar](max) NULL,
[Revision] [nvarchar](max) NULL
)
Every line has multiple revisions, so I just need to insert the last revision column value in FileISOManagers table inside Revision column in lines table automatically. I want do this in SQL Server. Is there any solution to do this? Should I use a trigger?
revision
-- Create trigger on table FileISOManagers for Insert statement
CREATE TRIGGER trgAfterInsert on FileISOManagers
FOR INSERT
AS declare #Revision varchar(100);
select #Revision=i.Revision from inserted i;
set #action='Inserted Record -- After Insert Trigger.'; insert into Lines(Revision)
values (#Revision);
PRINT 'AFTER INSERT trigger fired.
It can be done using joins and alias.
Example
INSERT INTO FileISOManagers (Revision ..columns)
SELECT *
FROM Lines ls
WHERE NOT EXISTS
(
SELECT *
FROM FileISOManagers
WHERE LineId = ls.id
) order by ls.id desc

MSSQL DateTime2 Fail to Update

Im running stored procedure which in charges to insert, update and delete table's entries.
While both insert and delete runs smoothly, the update operation updates all columns except DATETIME2 one.
The scenario - I test my Repository pattern (using C# code) in the following way:
delete the entire [BackgroundTaskAttachtment] table
Create 4 new entries
delete single entry created on step 2
Wait for 5 seconds
modify one of the entries
the result is having 3 entries in [BackgroundTaskAttachtment] table, with all properties set as expected, except the [UpdatedOnUtc] which not updated (it is equal to [CreatedOnUtc]
I marked the updated row (as you can see [FilePath] was successfully updated):
Would appreciate community insights,
Thank you
This is the stored procedure code:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SP_ArrangeBackgroundTaskAttachtments]
(
#backgroundTaskId BIGINT,
#taskAttchs [dbo].[BackgroundTaskAttachtmentType] READONLY
)
AS
BEGIN
SET NOCOUNT ON;
--delete all removed attachtments
DELETE FROM [BackgroundTaskAttachtment]
WHERE [BackgroundTaskId] = #backgroundTaskId AND [Id] NOT IN (SELECT [Id] FROM #taskAttchs)
----Update exist key-value pairs
UPDATE [dbo].[BackgroundTaskAttachtment]
SET
[IsPrimary] = attachs.[IsPrimary],
[FilePath] = attachs.[FilePath],
[Bytes] = attachs.[Bytes],
[UpdatedOnUtc] = GETUTCDATE()
FROM #taskAttchs AS attachs
WHERE attachs.[Id] = [BackgroundTaskAttachtment].[Id]
--insert new records
SELECT #backgroundTaskId AS [BackgroundTaskId], [FilePath], [IsPrimary], [Bytes], GETUTCDATE() AS [CreatedOnUtc], GETUTCDATE() AS [UpdatedOnUtc]
INTO #Temp FROM #taskAttchs as atcs
WHERE atcs.[Id] NOT IN (SELECT [Id] FROM [BackgroundTaskAttachtment] AS bta WHERE bta.[BackgroundTaskId] = #backgroundTaskId )
INSERT INTO [BackgroundTaskAttachtment]([BackgroundTaskId], [IsPrimary], [Bytes], [FilePath], [CreatedOnUtc], [UpdatedOnUtc] )
SELECT [BackgroundTaskId], [IsPrimary], [Bytes], [FilePath], [CreatedOnUtc], [UpdatedOnUtc]
FROM #Temp
END
This is the table type (sent from CLR to SQL)
CREATE TYPE [dbo].[BackgroundTaskAttachtmentType] AS TABLE(
[Id] [BIGINT] NOT NULL,
[FilePath] [NVARCHAR](MAX) NULL,
[IsPrimary] [BIT] NOT NULL,
[BackgroundTaskId] [BIGINT] NULL,
[Bytes] [VARBINARY](MAX) NULL
)
GO
this is the table definition
CREATE TABLE [dbo].[BackgroundTaskAttachtment]
(
[Id] BIGINT IDENTITY(1,1) NOT NULL,
[BackgroundTaskId] BIGINT NOT NULL,
[IsPrimary] BIT NOT NULL DEFAULT 0,
[FilePath] NVARCHAR(MAX) NULL,
[Bytes] VARBINARY(MAX) NULL,
[CreatedOnUtc] DATETIME2 NOT NULL,
[UpdatedOnUtc] DATETIME2 NOT NULL,
[RowVersion] ROWVERSION NOT NULL,
CONSTRAINT [PK_dbo.BackgroundTaskAttachtment] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.BackgroundTaskAttachtment_BackgroundTask_Id]
FOREIGN KEY ([BackgroundTaskId])
REFERENCES [dbo].[BackgroundTask] ([Id])
ON DELETE CASCADE
);
Please try using SYSUTCDATETIME which returns datetime2.
The GETUTCDATE which you are using, returns datetime.

How do I create a stored procedure that takes a list of products or a datatable as a param, then insert into a table?

I'm fairly new to SQL Server any input and advice would help greatly.
I have 3 tables which are in one-to-many relationships.
Table Person holds customer info
CREATE TABLE [dbo].[Person](
[PID] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [varchar](255) NULL,
[LastName] [varchar](255) NULL,
[CAddress] [varchar](255) NULL,
[Ccity] [varchar](255) NULL,
[Cstate] [varchar](2) NULL,
[Czipcode] [varchar](20) NULL,
[Ccountry] [varchar](255) NULL,
[Cphone] [varchar](25) NULL,
[Cemail] [varchar](255) NULL,
[CipAddress] [varchar](255) NULL)
Table Transaction holds their transaction
CREATE TABLE [dbo].[Transaction](
[TID] [int] IDENTITY(1,1) NOT NULL,
[PID] [int] NOT NULL,
[DateOfTransaction] [date] NULL)
with a third table, TransactionDetail, which holds transaction details
CREATE TABLE [dbo].[TransactionDetail](
[TDID] [int] IDENTITY(1,1) NOT NULL,
[TID] [int] NULL,
[ProductID] [int] NULL,
[ProductName] [varchar](255) NULL,
[ProductQTY] [int] NULL,
[ProductPrice] [decimal](18, 2) NULL)
I would like to create a stored procedure to insert once into the Person table then insert multiple details into the third table.
this is what i got i'm not sure if this is correct?
CREATE TYPE dbo.TransactionTableType AS TABLE
( TID int, ProductID int, ProductName varchar(255), ProductQTY int, ProductPrice decimal(18,2) )
go
CREATE PROCEDURE insertTransacion
#NewProduct dbo.TransactionTableType READONLY,
#FirstName varchar(255),
#LastName varchar(255),
#CAddress varchar(255),
#Ccity varchar(255),
#Cstate varchar(2),
#Czipcode varchar(20),
#Ccountry varchar(255),
#CPhone varchar(25),
#Cemail varchar(255),
#CipAddress varchar(255),
#DateOfTrans date
as
begin
SET NOCOUNT ON;
DECLARE #Pid int
insert into Person(FirstName,LastName,CAddress,Ccity,Cstate,Czipcode,Ccountry,Cphone,Cemail,CipAddress) values (#FirstName,#LastName,#CAddress,#Ccity,#Cstate,#Czipcode,#Ccountry,#CPhone,#Cemail,#CipAddress)
SET #Pid = SCOPE_IDENTITY()
insert into PTransactions(PID, DateOfTransaction) values (#Pid, #DateOfTrans)
DECLARE #Tid int
SET #Tid = SCOPE_IDENTITY()
insert into TransactionDetail(TID, ProductID, ProductName, ProductQTY, ProductPrice) Select #Tid, ntd.ProductID, ntd.ProductName, ntd.ProductQTY, ntd.ProductPrice from #NewProduct as ntd
end
Not sure how to do this in a stored procedure I know how to do it programmatically in asp.net using ado, however I'm trying to avoid that. Sorry for the grammar.
Short answer is you can't, although there are a couple of options open to you.
You can either create a SP to enter the person data, and a separate one to insert the data - one line at a time. Return the person id value with the first call and use that in the subsequent SP calls to insert the data. If you go down this path, make sure you wrap the calling code up in transaction objects so you can roll back the whole lot if you have a problem. You don't state what language you're using for the rest of your code?
Second option is to look at the SQL Bulk Insert command - this is best if you have a lot of data to add into the third table. But involves writing that data out to a file first - slight pain but it's then very fast. Very good if you have thousands or more rows to add.
Couple of other options out there as well depending on your development language.
Cheers
Simon

Resources