Roll directories up to parent - sql

Is there a way to roll data up that looks like this:
What I am looking for is this:
Y:\Data\FS02-V\Aetna\ETL | Data, development files
So rows two and three being children of row one should roll up to the parent and the parent should show all file types contained. I am working in sql server and this is the code that produces the source table:
SELECT
[MCL].[Category Description] As Category
, [SF].[Directory] as Directory
, CONVERT(BIGINT, [SF].[Length]) AS FileSize
, (SELECT MAX([Id]) FROM [dbo].[split](RIGHT([SF].[Directory],LEN([SF].[Directory])-1),'\')) AS [LevelsFromRoot]
FROM [dbo].[FS02V_SourceFiles] [SF]
INNER JOIN [dbo].[Extensions] [E]
ON [SF].[Extension] = [E].[Extension]
INNER JOIN [dbo].[MasterCategoryLookup] [MCL]
ON [MCL].CategoryID = [E].Category
ORDER BY [SF].[Directory]
Table def:
CREATE TABLE [dbo].[FS02V_SourceFiles](
[Length] [float] NULL,
[Directory] [nvarchar](255) NULL,
[Extension] [nvarchar](255) NULL,
[Type] [nvarchar](max) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
CREATE TABLE [dbo].[Extensions](
[Extension] [nvarchar](255) NULL,
[Type] [nvarchar](max) NULL,
[Category] [int] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Split function:
ALTER FUNCTION [dbo].[Split]
(
#RowData nvarchar(max),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Data nvarchar(max)
)
AS
BEGIN
Declare #Cnt int
Set #Cnt = 1
While (Charindex(#SplitOn,#RowData)>0)
Begin
Insert Into #RtnValue (data)
Select
Data = ltrim(rtrim(Substring(#RowData,1,Charindex(#SplitOn,#RowData)-1)))
Set #RowData = Substring(#RowData,Charindex(#SplitOn,#RowData)+1,len(#RowData))
Set #Cnt = #Cnt + 1
End
Insert Into #RtnValue (data)
Select Data = ltrim(rtrim(#RowData))
Return
END
The path(Directory) is stored as nvarchar and it should be the driver to roll up to the parent. I would assume the path needs to be split and my function is already doing something similar to get the path levels. I think this would be easier in SQL with my raw data but at the end of the day I am going to be visualizing this data in tableau so if anyone knows if its easier to use sql for this before I feed it into tableau or just use tableau with my source data I would try that as well.

Solution to this was simple:
Use abolve SQL to prepare the directories and group them with their respective files and then roll it up using Tableau and calculated fields.

Related

replace a computed column with a logic that works with INSERT

I have a table called tblPacks.
CREATE TABLE [dbo].[tblPacks]
(
[ID] [int] NOT NULL,
[BatchNumber] [varchar](30) NULL,
[PackID] VARCHAR(50),
[Status] [int] NULL
)
And a stored procedure spInsertPacks.
CREATE PROCEDURE spInsertPacks
#ID INT,
#BatchNumber VARCHAR(30),
#Count INT
AS
BEGIN
INSERT INTO tblPacks
Values
(
#ID,
#BatchNumber,
CONVERT([varchar](50),
'PK'+
case
when len(#ID)<=(3) then CONVERT([varchar](20),right((0.001)*#ID,(3)),0)
else CONVERT([varchar](20),#ID,0)
end,0),0)
END
If ID of data type INT inserted in an order like 1,2,3,4,5... the above logic works fine. But there is no restriction for a user to enter random numbers. I want a stored procedure to generate PackID(PK001,PK002..) sequence in order, irrespective of #ID and ID. Cannot be an identity Column. How can I do that?
Actually This PackID is a barcode If barcode already existed for Pack then that sequence may not be same with the sequence we used and Newly generated barcodes which we are generating will be in seuquence PK001
Sample Output:-
ID BatchNumber PackID Status
1 b1 PK001 0
1 b2 Pk002 0
5 b7 ABC768 0
3 b2 PK003 0
I have simplified the logic a bit for generating PackID
Add a new column(identifier) for identifying the code and use it for PackID generation and for sequence use Identity column
CREATE TABLE [dbo].[tblPacks]
(
Iden_ID INT IDENTITY(1, 1),
[ID] [INT] NOT NULL,
[BatchNumber] [VARCHAR](30) NULL,
[Identifier] [VARCHAR](50),
[PackID] AS [Identifier]
+ CASE
WHEN Iden_ID <= 999 THEN RIGHT('00' + CONVERT(VARCHAR(3), ID), 3)
ELSE CONVERT([VARCHAR](20), ID, 0)
END,
[Status] [INT] NULL
)
To check the working
INSERT INTO [dbo].[tblPacks]
([ID],identifier,[BatchNumber],[Status])
VALUES (1,'pk','bat',1)
SELECT *
FROM [tblPacks]

updated record is inserting into the history table not the old record

i have two tables Test and TestHistory
CREATE TABLE [dbo].[TEST](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Test_History](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
INSERT INTO TEST ([ID],[Name],[Status],[CreatedDate])values (1,'Mohan','A',GETDATE())
Created Trigger :
ALTER TRIGGER [dbo].[trg_Test]
ON [dbo].[TEST]
FOR UPDATE
AS
Declare #ID INT;
Declare #Name varchar(10);
Declare #Status CHAR(2);
Declare #CreatedDate DATETIME;
Select #ID = I.ID from INSERTED I
Select #Name = I.Name from INSERTED I
Select #Status = I.Status from INSERTED I
Select #CreatedDate = I.CreatedDate from INSERTED I
INSERT INTO [dbo].[Test_history]
([ID]
,[Name]
,[Status]
,[CreatedDate]
)
SELECT #ID,
#Name,
#Status,
GETDATE()
FROM INSERTED I
WHERE #ID = [ID]
When I'm updating the record like
Update [TEST] SET Status = 'I' then the old record with Status = 'A' should inserted but the what ever i'm updating it has been inserting into Testhistory table not the old record
where i'm doing wrong and how to insert old value
like if i updating Status = 'I' and in history table Status = 'A' shoul be inserted
You need to INSERT from DELETED not from INSERTED.
See examples here Understanding SQL Server inserted and deleted tables for DML triggers.
Like Karl mentioned, you need to refer deleted table for old updated row information. Apart from that your existing code doesn't work when you update more than a single row. What you need is something like this.
ALTER TRIGGER [dbo].[trg_Test]
ON [dbo].[TEST]
FOR UPDATE
AS
INSERT INTO [dbo].[Test_history]
(
[ID],
[Name],
[Status],
[CreatedDate]
)
SELECT ID,
Name,
Status,
GETDATE()
FROM deleted d

Inserted clause returns 0 when used with triggers

I'm trying to get the last inserted rows Id from an inserts statement on the following table using SQL server 2012
[dbo].[Table](
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NULL,
[CreatedDate] [datetime2](7) NOT NULL,
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED
(
[TableId] ASC
)
I'm also using an audit triggers on that table that are as follows:
trigger [dbo].[trigger_Table_auditColumnAutoInsert]
on [dbo].[Table]
instead of insert
/**************************************************************
* INSTEAD OF trigger on table [dbo].[Table] responsible
for automatically inserting audit column data
**************************************************************/
as
begin
set nocount on
declare #currentTime datetime2
set #currentTime = GETUTCDATE()
insert into [dbo].[Table]
(
Name,
CreatedBy,
CreatedDate,
ModifiedBy,
ModifiedDate
)
select
Name,
ISNULL(CreatedBy, system_user),
#currentTime,
NULL,
NULL
from inserted
select SCOPE_IDENTITY() as [TableId]
goto EOP -- end of procedure
ErrorHandler:
if (##trancount <> 0) rollback tran
EOP:
end
I used different approaches, but nothing 'SAFE' seems to work.
Using scope identity returns null
insert into dbo.[Table](Name) Values('foo')
select SCOPE_IDENTITY()
Using OUTPUT INSERTED always returns 0 for the identity coloumns; although it returns the other inserted values:
declare #tmpTable table
(
TableId int,
Name nvarchar (50)
)
INSERT INTO [dbo].[Table]([Name])
output inserted.TableId, inserted.Name into #tmpTable
VALUES('foo')
select * from #tmpTable
TableId Name
0 foo
I know of another solution to get the inserted Id from the triggers itself, by executing a dynamic sql command as follows:
declare #tmpTable table (id int)
insert #tmpTable (id )
exec sp_executesql N'insert into dbo.[Table](Name) Values(''foo'')'
select id from #tmpTable
I couldn't figure out why in the first 2 cases it is not working; why the SCOPE_IDENTITY() does not work although the triggers execute in the same transaction? And also why the INSERTED clause returns 0 for the identity column.
It appears that the following requirements apply to your audit column data:
Use the insert value supplied for CreatedBy, or use SYSTEM_USER by default.
Always use GETUTCDATE() for CreatedDate.
If the INSTEAD OF trigger (rather than an AFTER trigger) is not essential to your requirements, then you can use DEFAULT constraints on your audit columns and an AFTER INSERT trigger to enforce requirement #2.
CREATE TABLE [dbo].[Table]
(
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NOT NULL CONSTRAINT [DF_Table_CreatedBy] DEFAULT SYSTEM_USER,
[CreatedDate] [datetime2](7) NOT NULL CONSTRAINT [DF_Table_CreatedDate] DEFAULT GETUTCDATE(),
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED ([TableId] ASC)
)
GO
CREATE TRIGGER Trigger_Table_AfterInsert ON [dbo].[Table]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
UPDATE [dbo].[Table] SET [CreatedDate]=GETUTCDATE()
FROM [dbo].[Table] AS T
INNER JOIN INSERTED AS I ON I.[TableId]=T.[TableId]
END
GO
Then, both SCOPE_IDENTITY() and OUTPUT INSERTED techniques to get the new TableId value work as expected.
If the INSTEAD OF trigger is essential to your implementation, then SELECT ##IDENTITY is an alternative to SCOPE_IDENTITY.

How to join data using dynamic table and field names

So I have some tables that contains a foreign key that refers to different tables and fields depending on the context. I have a key source table that contains the relevant table and field names. Then I need to be able to lookup the values in those tables/fields.
I simplified it to the following tables/data for demonstration of what I have:
CREATE TABLE [dbo].[WorkOrderStatus](
[WorkOrderStatusId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](60) NOT NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[Note](
[NoteId] [int] IDENTITY(1,1) NOT NULL,
[NoteKeySourceId] [int] NULL,
[KeyValue] [int] NULL,
[Note] [nvarchar](max) NOT NULL,
) ON [PRIMARY]
CREATE TABLE [dbo].[NoteKeySource](
[NoteKeySourceId] [int] IDENTITY(1,1) NOT NULL,
[SourceTable] [nvarchar](60) NOT NULL,
[SourceField] [nvarchar](60) NOT NULL,
[SourceTextField] [nvarchar](60) NOT NULL,
) ON [PRIMARY]
GO
INSERT INTO WorkOrderStatus VALUES('Open')
DECLARE #OpenStatusId int = SCOPE_IDENTITY();
INSERT INTO WorkOrderStatus VALUES('Closed')
DECLARE #ClosedStatusId int = SCOPE_IDENTITY();
INSERT INTO NoteKeySource VALUES('WorkOrderStatus', 'WorkOrderStatusId', 'Name')
DECLARE #WorkOrderSource int = SCOPE_IDENTITY();
INSERT INTO Note VALUES(#WorkOrderSource, #OpenStatusId, 'Opened the work order')
INSERT INTO Note VALUES(#WorkOrderSource, #ClosedStatusId, 'Closed the work order')
So I need to be able to query a list of Notes, including the SourceTextField referred to in NoteKeySource, identified by KeyValue.
Here is the SP that I have now, which works:
CREATE PROCEDURE [dbo].[spGetNotes]
AS
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
CREATE TABLE #NoteTable (
NoteId int NOT NULL,
SourceTable nvarchar(60),
SourceField nvarchar(60),
SourceTextField nvarchar(60),
KeyValue int,
Context nvarchar(MAX),
Note nvarchar(MAX),
)
INSERT INTO #NoteTable (NoteId, SourceTable, SourceField, SourceTextField, KeyValue, Note)
SELECT N.NoteId, NKS.SourceTable, NKS.SourceField, NKS.SourceTextField, N.KeyValue, N.Note
FROM Note AS N
LEFT OUTER JOIN NoteKeySource AS NKS
ON NKS.NoteKeySourceId = N.NoteKeySourceId
DECLARE #Context nvarchar(max)
DECLARE #Sql nvarchar(255);
DECLARE #SqlDecl nvarchar(255) = N'#Key int, #Ctx nvarchar(MAX) OUTPUT';
DECLARE #NoteId int, #KeyValue int, #SourceTable nvarchar(60), #SourceField nvarchar(60), #SourceTextField nvarchar(60)
DECLARE db_cursor CURSOR FOR SELECT SourceTable, SourceField, SourceTextField, KeyValue
FROM #NoteTable WHERE SourceTable IS NOT NULL FOR UPDATE OF Context
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #SourceTable, #SourceField, #SourceTextField, #KeyValue
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Sql = N'SELECT #Ctx = ' + #SourceTextField + ' FROM ' + #SourceTable + ' WHERE ' + #SourceField + '= #Key';
EXECUTE sp_executesql #Sql, #SqlDecl, #Key = #KeyValue, #Ctx = #Context OUTPUT
UPDATE #NoteTable SET Context=#Context WHERE CURRENT OF db_cursor
FETCH NEXT FROM db_cursor INTO #SourceTable, #SourceField, #SourceTextField, #KeyValue
END
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT NoteId, Context, Note from #NoteTable
DROP TABLE #NoteTable
RETURN 0
Calling spGetNotes returns:
1:Open:Opened the work order
2:Closed:Closed the work order
It works, but it is pretty ugly, and I would just like to know if there is a better/cleaner/more appropriate way to do this without using a cursor or a while loop. It is my understanding that dynamic SQL cannot be used in a subquery of normal SQL, so this was the best I could come up with.
I am relatively green when it comes to SQL, so if I am wrong about that, please enlighten me! Also, if there is a better overall approach to the general domain problem, that would also be welcome and appreciated :)
Create a view that provides a single EAV overlay across your source tables. Then join NoteKeySource to it.
CREATE VIEW AllSources AS (
SELECT
'SourceTable1' AS [SourceTable], [SourceField], [SourceTextField]
FROM SourceTable1
UNPIVOT([SourceTextField] FOR [SourceField] IN
('Table1Field1','Table1Field2','Table1Field3','Table1Field4')
) p
UNION ALL
SELECT
'SourceTable2' AS [SourceTable], [SourceField], [SourceTextField]
FROM SourceTable2
UNPIVOT([SourceTextField] FOR [SourceField] IN
('Table2Field1','Table2Field2')
) p
UNION ALL
SELECT
'SourceTable3' AS [SourceTable], [SourceField], [SourceTextField]
FROM SourceTable3
UNPIVOT([SourceTextField] FOR [SourceField] IN
('Table3Field1','Table3Field2','Table3Field3')
) p
--etc
)

drop rows in a user defined table type

i have the following type:
CREATE TYPE [dbo].[locationTable] AS TABLE(
[location_id] [varchar](100) NULL,
[name] [nvarchar](100) NULL,
[address] [varchar](100) NULL,
[latitude] [varchar](100) NULL,
[longitude] [varchar](100) NULL,
[distance] [varchar](100) NULL,
[state] [varchar](100) NULL,
[sub_cat] [varchar](100) NULL,
[idCat] [varchar](100) NULL,
[icon_link] [varchar](100) NULL,
[checkinsCount] [int] NULL
)
GO
and i'm passing a table as parameter having the above type to a stored procedure... but i need to delete some rows from this table in my stored procedure but i keep getting that it cannot be modified.... sql is always requesting to define the table as readonly and in this case i can not modify
A table parameter to a stored procedure must be readonly. MSDN says:
Note that the READONLY keyword is required for declaring a table-valued parameter.
You can solve this dilemma by copying the content to a local table variable. For example:
if exists (select * from sys.procedures where name = 'TestProc')
drop procedure TestProc
if exists (select * from sys.types where name = 'TestType')
drop type TestType
go
create type TestType as table (id int, name varchar(20))
go
create procedure dbo.TestProc
#par TestType readonly
as
declare #t TestType
insert #t select * from #par
delete #t where id = 2
select * from #t
go
declare #p1 TestType
insert #p1 values (1,'a'), (2,'b'), (3,'c');
exec dbo.TestProc #p1