Flatten recursive data - tsql
I have a table glclassifications (Exact software) that looks like this:
CREATE TABLE [glclassifications](
[ID] [nvarchar](500) NULL, --(contains uniqueidentifier)
[Code] [nvarchar](500) NULL,
[Description] [nvarchar](500) NULL,
[Parent] [nvarchar](500) NULL --(contains uniqueidentifier)
)
It holds classifications that are hierarchical (hence the Parent column that refers to the ID of another record.)
I need this table to be 'flattened' so that I have
Parent_ID,
Parent_code,
Parent_description,
--child1
Child1_ID,
Child1_code,
Child1_description,
--child2
Child2_ID....
-- up until including child9
and so on.
My predecessor left me with this query that violates a lot of the designprinciples that we agreed upon. One of those is not using (temp)tables. I would like to simplify this script and preferably get the output as a view and not as a table. A catch is that the resulting view needs to deliver columns for 8 children, even if there are less in the table...
/* drop duplicates in glclassifications */
WITH CTE AS (
SELECT ROW_NUMBER() OVER (
PARTITION BY ID
ORDER BY ( SELECT 0)
) RN
FROM [dbo].[glclassifications]
)
DELETE FROM CTE
WHERE RN > 1;
/* drop tmp table */
DROP TABLE IF EXISTS #H;
CREATE TABLE #H (
Code varchar(100),
Description varchar(250),
ID varchar(100),
Division int
)
/* declare table */
DECLARE #n INT = 1
DECLARE #Sql varchar(50)
WHILE #n < 10 BEGIN
DECLARE #col_name varchar(10) = CONCAT('Code_' , #n)
SET #Sql = 'ALTER TABLE #H ADD ' + QUOTENAME(#col_name) + ' varchar(20) NULL'
EXEC (#Sql)
SET #col_name = CONCAT('Name_' , #n)
SET #Sql = 'ALTER TABLE #H ADD ' + QUOTENAME(#col_name) + ' varchar(250) NULL'
EXEC (#Sql)
SET #n = #n + 1
END
/* insert level 1 */
INSERT INTO #H (Code,[Description],ID,Division,Code_1, Name_1)
SELECT
Code,Description,ID,bip_office,Code,Description
FROM
[dbo].[glclassifications]
WHERE Parent = ''
/* insert level 2 */
INSERT INTO #H (Code,[Description],ID,Division,Code_1, Name_1, Code_2, Name_2)
SELECT
e.Code,e.Description,e.Id,e.bip_office,Code_1,Name_1,e.Code,e.Description
FROM
[dbo].[glclassifications] e
JOIN #H h on h.ID = e.Parent
WHERE e.ID not in (SELECT DISTINCT ID from #H ) AND Parent IS NOT NULL ;
/* insert level 3 */
INSERT INTO #H (Code,[Description],ID,Division,Code_1, Name_1, Code_2, Name_2,Code_3, Name_3)
SELECT
e.Code,e.Description,e.Id,e.bip_office, Code_1,Name_1,Code_2, Name_2,e.Code,e.Description
FROM
[dbo].[glclassifications] e
JOIN #H h on h.ID = e.Parent
WHERE e.ID not in (SELECT DISTINCT ID from #H ) AND Parent IS NOT NULL ;
/* insert level 4 */
INSERT INTO #H (Code,[Description],ID,Division,Code_1, Name_1, Code_2, Name_2,Code_3, Name_3, Code_4, Name_4)
SELECT
e.Code,e.Description,e.Id,e.bip_office, Code_1,Name_1,Code_2, Name_2,Code_3, Name_3,e.Code,e.Description
FROM
[dbo].[glclassifications] e
JOIN #H h on h.ID = e.Parent
WHERE e.ID not in (SELECT DISTINCT ID from #H ) AND Parent IS NOT NULL ;
/* insert level 5 */
INSERT INTO #H (Code,[Description],ID,Division,Code_1, Name_1, Code_2, Name_2,Code_3, Name_3, Code_4, Name_4, Code_5, Name_5)
SELECT
e.Code,e.Description,e.Id,e.bip_office,Code_1,Name_1,Code_2, Name_2,Code_3, Name_3,Code_4, Name_4,e.Code,e.Description
FROM
[dbo].[glclassifications] e
JOIN #H h on h.ID = e.Parent
WHERE e.ID not in (SELECT DISTINCT ID from #H ) AND Parent IS NOT NULL ;
/* insert level 6 */
INSERT INTO #H (Code,[Description],ID,Division,Code_1, Name_1, Code_2, Name_2,Code_3, Name_3, Code_4, Name_4, Code_5, Name_5,Code_6, Name_6)
SELECT
e.Code,e.Description,e.Id,e.bip_office,Code_1,Name_1,Code_2, Name_2,Code_3, Name_3,Code_4, Name_4,Code_5, Name_5,e.Code,e.Description
FROM
[dbo].[glclassifications] e
JOIN #H h on h.ID = e.Parent
WHERE e.ID not in (SELECT DISTINCT ID from #H ) AND Parent IS NOT NULL ;
/* insert level 7 */
INSERT INTO #H (Code,[Description],ID,Division,Code_1, Name_1, Code_2, Name_2,Code_3, Name_3, Code_4, Name_4, Code_5, Name_5,Code_6, Name_6,Code_7, Name_8)
SELECT
e.Code,e.Description,e.Id,e.bip_office,Code_1,Name_1,Code_2, Name_2,Code_3, Name_3,Code_4, Name_4,Code_5, Name_5,Code_6, Name_6,e.Code,e.Description
FROM
[dbo].[glclassifications] e
JOIN #H h on h.ID = e.Parent
WHERE e.ID not in (SELECT DISTINCT ID from #H ) AND Parent IS NOT NULL;
/* store to table */
DROP TABLE IF EXISTS [dbo].[gb-hierarchie];
SELECT hie.*
INTO [dbo].[gb-hierarchie]
FROM #H hie
I did this
WITH cte_org AS (
SELECT
ID,
Code,
Description,
Parent
FROM
[dbo].[glclassifications]
WHERE len(parent) = 0
UNION ALL
SELECT
e.ID,
e.Code,
e.Description,
e.parent
FROM
[dbo].[glclassifications] e
INNER JOIN cte_org o
ON o.ID = e.parent
)
SELECT * FROM cte_org;
but how to continue?
With thanks to Bill Jetzer: simply joining the tables on itself 8 times was the easiest way to get these results
Related
Issue with PK violation on insert
I have a scenario where almost all of the tables have issues with the PK value as follows. This results is a database error or the violation of the PK insert. When using the DBCC CheckIdent it displays an inconsistency between the next value and the current one. Can anyone have a reason for the mismatch happening on several tables? Since this database is then replicate, I'm afraid this error will propagate across the environment. I adapted this script to fix it, but really trying to figure out the root of the problem. /** Version 3.0 **/ if object_id('tempdb..#temp') is not null drop table #temp ; with cte as ( SELECT distinct A.TABLE_CATALOG AS CATALOG, A.TABLE_SCHEMA AS "SCHEMA", A.TABLE_NAME AS "TABLE", B.COLUMN_NAME AS "COLUMN", IDENT_SEED (A.TABLE_NAME) AS Seed, IDENT_INCR (A.TABLE_NAME) AS Increment, IDENT_CURRENT (A.TABLE_NAME) AS Curr_Value , DBPS.row_count AS NumberOfRows FROM INFORMATION_SCHEMA.TABLES A inner join INFORMATION_SCHEMA.COLUMNS B on b.TABLE_NAME = a.TABLE_NAME and b.TABLE_SCHEMA = a.TABLE_SCHEMA inner join sys.identity_columns IC on OBJECT_NAME (IC.object_id) = a.TABLE_NAME inner join sys.dm_db_partition_stats DBPS ON DBPS.object_id =IC.object_id inner join sys.indexes as IDX ON DBPS.index_id =IDX.index_id WHERE A.TABLE_CATALOG = B.TABLE_CATALOG AND A.TABLE_SCHEMA = B.TABLE_SCHEMA AND A.TABLE_NAME = B.TABLE_NAME AND COLUMNPROPERTY (OBJECT_ID (B.TABLE_NAME), B.COLUMN_NAME, 'IsIdentity') = 1 AND OBJECTPROPERTY (OBJECT_ID (A.TABLE_NAME), 'TableHasIdentity') = 1 AND A.TABLE_TYPE = 'BASE TABLE' ) select 'DBCC CHECKIDENT ('''+A.[SCHEMA]+'.'+a.[TABLE]+''', reseed)' command , ROW_NUMBER() OVER(ORDER BY a.[SCHEMA], a.[TABLE] asc) AS ID , A.Curr_Value , a.[TABLE] into #temp from cte A ORDER BY A.[SCHEMA], A.[TABLE] declare #i int = 1, #count int = (select max(ID) from #temp) declare #text varchar(max) = '' select #COUNT= count(1) FROM #temp WHILE #I <= #COUNT BEGIN SET #text = (SELECT command from #temp where ID=#I) EXEC (#text + ';') print #text select Curr_Value OldValue, ident_current([TABLE]) FixValue, [TABLE] from #temp where ID=#I SET #I = #I + 1 SET #text=''; END go
maybe someone or something with enough permissions made a mistake by reseeding? As simple as this: create table testid ( id int not null identity (1,1) primary key, data varchar (3) ) insert into testid (data) values ('abc'),('cde') DBCC CHECKIDENT ('testid', RESEED, 1) insert into testid (data) values ('bad')
WHERE in NOT EXISTS clause being ignored
I'm trying to fill a table with rows that should be there: If a city in #Maps does not exist in #Results, then I will fill it using NOT EXISTS. The issue is that the filterisused = 1 not only is ignored, it seems to void the NOT EXISTS. With IsUsed = 1, everything in #Maps will be inserted to #Results regardless if it exists or not. If I remove IsUsed = 1, both rows from NY are inserted (correct behavior but not what I'm looking for). Here's the code: declare #Maps table ( Name varchar(20), IsUsed bit, Code varchar(20) ) insert into #Maps select 'NY', 1, 'NY1' union select 'NY', 0, 'NY2' union select 'FL', 0, 'FL1' union select 'TX', 0, 'TX1' declare #Results table ( Name varchar(20), Value int, Code varchar(20) ) insert into #results select 'FL', 12, 'FL1' union select 'TX', 54,'TX1' union select 'CA', 54,'CA1' union select 'NJ', 54,'NJ1' insert into #results select Name, 999, code from #Maps m -- This adds everything even if it exists where not exists (select name from #Results p where p.name = m.name and IsUsed = 1) -- This adds both 'NY'. Partially correct but adds column IsUsed = 0 -- where not exists (select name from #Results p where p.name = m.name) select * from #results How can I add the one row that's not included in #results and has IsUsed equal to 1? In this case it would be {'NY', 1, 'NY1}`. I understand that there are many ways of accomplishing this, but I'm interested in knowing how the where clause in not exists work.
You have to remove the IsUsed=1 from the NOT EXISTS and add it to the WHERE: insert into #results select Name, 999, code from #Maps m where m.IsUsed = 1 and not exists (select name from #Results p where p.name = m.name)
I think you confuse how an insert select works. The select is run independently. The insert is not committed until the end of the statement. See all the inserted cnt is 4. declare #maps table(name varchar(10), isUsed bit, code varchar(10)); insert into #Maps values ('NY', 1, 'NY1') , ('NY', 0, 'NY2') , ('FL', 0, 'FL1') , ('TX', 0, 'TX1') declare #Results table (Name varchar(20), Value int, Code varchar(20), cnt int) insert into #results values ('FL', 12, 'FL1', null) , ('TX', 54, 'TX1', null) , ('CA', 54, 'CA1', null) , ('NJ', 54, 'NJ1', null) select * from #results; insert into #Results select m.Name, 999, m.code , (select count(*) from #results) as cnt from #Maps m where not exists (select name from #Results p where p.name = m.name and m.IsUsed = 1) select * from #results; On the first NY where p.name = m.name is false so not exits is true On the second NY where p.name = m.name is false so not exits is true The first NY as not been committed On the FL and TL the where p.name = m.name is true but m.IsUsed = 1 is false so not exits is true
I am getting Dollar sign unterminated
I want to create a function like below which inserts data as per the input given. But I keep on getting an error about undetermined dollar sign. CREATE OR REPLACE FUNCTION test_generate ( ref REFCURSOR, _id INTEGER ) RETURNS refcursor AS $$ DECLARE BEGIN DROP TABLE IF EXISTS test_1; CREATE TEMP TABLE test_1 ( id int, request_id int, code text ); IF _id IS NULL THEN INSERT INTO test_1 SELECT rd.id, r.id, rd.code FROM test_2 r INNER JOIN raw_table rd ON rd.test_2_id = r.id LEFT JOIN observe_test o ON o.raw_table_id = rd.id WHERE o.id IS NULL AND COALESCE(rd.processed, 0) = 0; ELSE INSERT INTO test_1 SELECT rd.id, r.id, rd.code FROM test_2 r INNER JOIN raw_table rd ON rd.test_2_id = r.id WHERE r.id = _id; END IF; DROP TABLE IF EXISTS tmp_test_2_error; CREATE TEMP TABLE tmp_test_2_error ( raw_table_id int, test_2_id int, error text, record_num int ); INSERT INTO tmp_test_2_error ( raw_table_id, test_2_id, error, record_num ) SELECT DISTINCT test_1.id, test_1.test_2_id, 'Error found ' || test_1.code, 0 FROM test_1 WHERE 1 = 1 AND data_origin.id IS NULL; INSERT INTO tmp_test_2_error SELECT DISTINCT test_1.id, test_1.test_2_id, 'Error found ' || test_1.code, 0 FROM test_1 INNER JOIN data_origin ON data_origin.code = test_1.code WHERE dop.id IS NULL; DROP table IF EXISTS test_latest; CREATE TEMP TABLE test_latest AS SELECT * FROM observe_test WHERE 1 = 2; INSERT INTO test_latest ( raw_table_id, series_id, timestamp ) SELECT test_1.id, ds.id AS series_id, now() FROM test_1 INNER JOIN data_origin ON data_origin.code = test_1.code LEFT JOIN observe_test o ON o.raw_table_id = test_1.id WHERE o.id IS NULL; CREATE TABLE latest_observe_test as Select * from test_latest where 1=0; INSERT INTO latest_observe_test ( raw_table_id, series_id, timestamp, time ) SELECT t.id, ds.id AS series_id, now(), t.time FROM test_latest t WHERE t.series_id IS DISTINCT FROM observe_test.series_id; DELETE FROM test_2_error re USING t WHERE t.test_2_id = re.test_2_id; INSERT INTO test_2_error (test_2_id, error, record_num) SELECT DISTINCT test_2_id, error, record_num FROM tmp_test_2_error ORDER BY error; UPDATE raw_table AS rd1 SET processed = case WHEN tre.raw_table_id IS null THEN 2 ELSE 1 END FROM test_1 tr LEFT JOIN tmp_test_2_error tre ON tre.raw_table_id = tr.id WHERE rd1.id = tr.id; OPEN ref FOR SELECT 1; RETURN ref; OPEN ref for SELECT o.* from observe_test o ; RETURN ref; OPEN ref FOR SELECT rd.id, ds.id AS series_id, now() AS timestamp, rd.time FROM test_2 r INNER JOIN raw_table rd ON rd.test_2_id = r.id INNER JOIN data_origin ON data_origin.code = rd.code WHERE o.id IS NULL AND r.id = _id; RETURN ref; END; $$ LANGUAGE plpgsql VOLATILE COST 100; I am not able to run this procedure. Can you please help me where I have done wrong?
I am using squirrel and face the same question as you. until I found that: -- Note that if you want to create the function under Squirrel SQL, -- you must go to Sessions->Session Properties -- then SQL tab and change the Statement Separator from ';' to something else -- (for intance //). Otherwise Squirrel SQL sends one piece to the server -- that stops at the first encountered ';', and the server cannot make -- sense of it. With the separator changed as suggested, you type everything -- as above and end with -- ... -- end; -- $$ language plpgsql -- // -- -- You can then restore the default separator, or use the new one for -- all queries ... --
How can I use sp_spaceused on specific tables?
I'm trying to call sp_spaceused to get the number of rows in a few specific tables that I want to monitor, but I'm trying to avoid using a cursor. I've made a table to hold all the tables I want to monitor: CREATE TABLE MonitoredTable ( MonitoredTableID INT PRIMARY KEY IDENTITY(1, 1) NOT NULL , DatabaseName NVARCHAR(128) NOT NULL , SchemaName NVARCHAR(128) NOT NULL , TableName NVARCHAR(128) NOT NULL , RowNumberThreshold INT NOT NULL , IsActive BIT NOT NULL ) My problem is: I want to create a function that will only return MonitoredTableIDs for tables that their row counts exceed the defined RowNumberThreshold. Here's what I'd like to do, but this is invalid SQL: CREATE FUNCTION dbo.GetTablesLargerThanThreshold() RETURNS #tablesLargerThanThreshold TABLE ( MonitoredTableID INT NOT NULL ) AS BEGIN INSERT INTO #tablesLargerThanThreshold SELECT MonitoredTableID FROM MonitoredTable WHERE IsActive = 1 AND (SELECT [rows] FROM (EXEC sp_spaceused DatabaseName + '.' + SchemaName + '.' + TableName)) > RowNumberThreshold RETURN END Is there a way I can check if the number of rows in a MonitoredTable are greater than the defined RowNumberThreshold without resorting to a cursor?
Something like this? -- See the following pages for documentation on the tables used in this query: -- -- sys.indexes https://msdn.microsoft.com/en-us/library/ms173760.aspx -- sys.partitions https://msdn.microsoft.com/en-us/library/ms175012.aspx -- sys.allocation_units https://msdn.microsoft.com/en-us/library/ms189792.aspx -- sys.tables Only columns inherited from sys.object, see link below -- sys.object https://msdn.microsoft.com/en-us/library/ms190324.aspx SELECT OBJECT_NAME(i.OBJECT_ID) AS [TableName] , p.[rows] AS [Num_Rows] FROM sys.indexes AS i INNER JOIN sys.partitions AS p ON p.OBJECT_ID = i.OBJECT_ID AND p.index_id = i.index_id INNER JOIN sys.allocation_units AS a ON a.container_id = p.partition_id INNER JOIN sys.tables AS t ON i.OBJECT_ID = t.OBJECT_ID WHERE i.type <= 1 -- Heap or clustered index AND a.type = 1 -- In-row data AND t.type = 'U' -- User-defined table AND t.is_ms_shipped = 0 -- sys.object was not created by an internal SQL Server component
Exlude row with same key from 1st select if exists in 2nd select statement in UNION
I have two tables Part and Service Entry Part. The first statement returns all parts and the second statement returns parts with a service type of 13. What I need to do is if the second select returns a part then the record from second select should be inlcuded and that part from the first select should be be excluded DECLARE #run_log TABLE( [Instrument] nvarchar(max), [SubSystem] NVARCHAR(max), [AbPartNumber] NVARCHAR(max), [RSLMSPartID] int, [PartDescriptionWithParent] NVARCHAR(max), [PartRevisionNumber] NVARCHAR(max), [ServiceEntryID] int, [Date] datetime, [TSBNumber] nvarchar (max), [CRNumber] nvarchar(max) ) insert #run_log -- parts with default revision number select System.SystemFullName as Instrument, Part.System as SubSystem, Part.AbPartNo as AbPartNumber, Part.ID as RSLMSPartID, Part.PartDescriptionWithParent, Part.RevisionNumber as PartRevisionNumber, NULL as ServiceEntryID, NULL as Date, NULL as TSBNumber, NULL as CRNumber from Part inner join InstrumentType on Part.InstrumentTypeID = InstrumentType.ID inner join SystemModule on SystemModule.InstrumentTypeID = Part.InstrumentTypeID inner join System on System.ID = SystemModule.SystemID WHERE ((#PlatformID =0) OR (System.PlatformID = #PlatformID) OR (#PlatformID = 12 AND System.PlatformID <= 2)) AND (#SelectedSystemID is null OR System.ID IN(select * from dbo.SplitInts_RBAR_1(#SelectedSystemID, ','))) AND (#SelectedAbIDs is null OR Part.ID IN (select * from dbo.SplitInts_RBAR_1(#SelectedAbIDs,','))) AND (#SelectedSubSystems is null OR Part.System IN (select * from dbo.SplitStrings_Moden(#SelectedSubSystems,','))) AND System.Active = 1 and Part.Active = 1 ORDER BY SubSystem,RSLMSPartID ;WITH RunLogs AS( SELECT * FROM #run_log r ) select * from RunLogs UNION select System.SystemFullName as Instrument, Part.System as Subsystem, Part.AbbottPartNo as AbPartNumber, Part.ID as RSLMSPartID, Part.PartDescriptionWithParent, ServiceEntryPart.PartRevisionNumber AS PartRevisionNumber, ServiceEntryPart.ServiceEntryID, ServiceEntry.ServiceDateTime as Date, ServiceEntry.TSBNumber, ServiceEntry.CRNumber from Part inner join ServiceEntryPart on ServiceEntryPart.PartID = Part.ID inner join ServiceEntry on ServiceEntry.ID = ServiceEntryPart.ServiceEntryID inner join systemmodule on ServiceEntryPart.SystemModuleID = SystemModule.ID inner join System on System.ID = SystemModule.SystemID cross apply dbo.SplitStrings_Moden(ServiceEntryPart.ServiceTypeIDs, N',') M2 JOIN dbo.SplitStrings_Moden('13', N',') P ON (M2.Item = P.Item) WHERE System.Active = 1 AND Part.Active = 1 AND (#SelectedSystemID is null OR System.ID IN(select * from dbo.SplitInts_RBAR_1(#SelectedSystemID, ','))) AND ((#PlatformID =0) OR (System.PlatformID = #PlatformID) OR (#PlatformID = 12 AND System.PlatformID <= 2)) AND (ServiceEntry.ServiceDateTime between #StartDate and #EndDate) AND (#SelectedAbIDs is null OR Part.ID in (select * from dbo.SplitInts_RBAR_1(#SelectedAbIDs,','))) AND (#SelectedSubSystems is null OR Part.System IN (select * from dbo.SplitStrings_Moden(#SelectedSubSystems,',')))