Microsoft SQL Server : splitting data and cross join - counter table - stuck - sql-server

I am using Microsoft SQL.
First thing let me explain that the "splitter" command in this SQL script works when my data comes in without all the cn's and paths and xml data. If the data comes in as
testrole|testrole2|testrole3
I am trying to get it to work while bringing the data in raw. It will still come in pipe delimited but with a lot more string data after the role/ access name..So I can use it later for scripting in bulk.
I want the end result to be user ID - Role and also show duplicate user IDs for users with more than one role.
1 - TestRole
2 - TestRole
2 - TestApp
3 - TestApp
Any ideas on how to get this to work? You can see some users will have one role and other multiple.
if OBJECT_ID ('tempdb.dbo.#RolesStage2') IS NOT NULL
DROP TABLE #RolesStage2
DECLARE #CountTable TABLE (UserID BIGINT)
INSERT INTO #CountTable
VALUES (1), (2), (3), (4)
DECLARE #TempUserObjects TABLE
(
UserID BIGINT,
AssignedRoles NVARCHAR(MAX)
)
INSERT INTO #TempUserObjects (UserID, AssignedRoles)
VALUES
(1, 'cn=TestingApp1,cn=Application,cn=Base,cn=Level2,cn=Definitions,cn=Configuration,cn=Drivers,cn=UserApplication,cn=FakePath,<assignment><start_tm>123456789575</start_tm><req_tm>1234568789854</req_tm><req>cn=FakeAdmin,ou=Admin,ou=ESC,o=authorizationpathfake</req><req_desc>Import - Child Request</req_desc></assignment>|'),
(2, 'cn=TestingApp2,cn=Application,cn=Base,cn=Level2,cn=Definitions,cn=Configuration,cn=Drivers,cn=UserApplication,cn=FakePath,<assignment><start_tm>123456789575</start_tm><req_tm>1234568789854</req_tm><req>cn=FakeAdmin,ou=Admin,ou=ESC,o=authorizationpathfake</req><req_desc>Import - Child Request</req_desc></assignment>|'),
(3, 'cn=TestingApp3,cn=Application,cn=Base,cn=Level2,cn=Definitions,cn=Configuration,cn=Drivers,cn=UserApplication,cn=FakePath,<assignment><start_tm>123456789575</start_tm><req_tm>1234568789854</req_tm><req>cn=FakeAdmin,ou=Admin,ou=ESC,o=authorizationpathfake</req><req_desc>Import - Child Request</req_desc></assignment>|cn=RoleTest3,cn=Application,cn=Base,cn=Level2,cn=Definitions,cn=Configuration,cn=Drivers,cn=UserApplication,cn=FakePath,<assignment><start_tm>123456789575</start_tm><req_tm>1234568789854</req_tm><req>cn=FakeAdmin,ou=Admin,ou=ESC,o=authorizationpathfake</req><req_desc>Import - Child Request</req_desc></assignment>'),
(4, 'cn=RoleTest1,cn=Application,cn=Base,cn=Level2,cn=Definitions,cn=Configuration,cn=Drivers,cn=UserApplication,cn=FakePath,<assignment><start_tm>123456789575</start_tm><req_tm>1234568789854</req_tm><req>cn=FakeAdmin,ou=Admin,ou=ESC,o=authorizationpathfake</req><req_desc>Import - Child Request</req_desc></assignment>|cn=RoleTest5,cn=Application,cn=Base,cn=Level2,cn=Definitions,cn=Configuration,cn=Drivers,cn=UserApplication,cn=FakePath,<assignment><start_tm>123456789575</start_tm><req_tm>1234568789854</req_tm><req>cn=FakeAdmin,ou=Admin,ou=ESC,o=authorizationpathfake</req><req_desc>Import - Child Request</req_desc></assignment>')
SELECT
*
FROM
#TempUserObjects
--Splitter Statement
SELECT
tuo.UserID,
SUBSTRING('|' + tuo.AssignedRoles + '|', ct.UserID + 1, CHARINDEX('|', '|' + tuo.AssignedRoles + '|', ct.UserID + 1) - ct.UserID - 1) AS 'NRFAssignedRoles'
INTO
#RolesStage2
FROM
#CountTable ct
CROSS JOIN
#TempUserObjects tuo
WHERE
ct.UserID < LEN('|' + tuo.AssignedRoles + '|')
AND SUBSTRING('|' + tuo.AssignedRoles + '|', ct.UserID, 1) = '|'
--Shows SubstringCharindex works
SELECT
SUBSTRING(AssignedRoles, 4, CHARINDEX(',', AssignedRoles) - 4) AS 'SubstringCharindex'
FROM
#TempUserObjects
SELECT *
FROM #RolesStage2

Related

SQL Server job gets stuck occasionally

I found my SQL Server job gets stuck occasionally, about once every two months. Since I am not from DBA background, I need some to help to rectify the issue.
So far, I have tried to pinpoint the issue by checking the activity monitor. I found the issue is caused by one of my stored procedures which it will create a temp table to collect data, then the data will be inserted into one of my transaction tables. This table have 400 millions of records.
Whenever this issue occur, I stop the job and:
I rerun the job, the stored procedure can complete
I execute the stored procedure manually, the stored procedure completes
I implemented the SP_BlitzCache, and execute it. I can see it suggest DBCC FREEPROCCACHE (0x0...) on the stored procedure.
CREATE TABLE #dtResult
(
RunningNumber INTEGER,
, AlphaID BIGINT
, BetaID BIGINT
, Content varchar(100)
, X varchar(10)
, Y varchar(10)
)
INSERT INTO #dtResult ( RunningNumber, ...)
SELECT RowId AS RunningNumber,
...
FROM
...
/*** Based on activity monitor, the highest CPU caused by this statement ***/
INSERT INTO tblTransaction ( ... )
SELECT DISTINCT
RES.AlphaID
, b.UnitId
, RES.BetaID
, CASE WHEN RES.BinData IS NULL THEN [dbo].[fnGetCode](B.Data, RES.X, RES.Y) ELSE RES.Content END
, CONVERT(DATETIME, SUBSTRING(RES.Timestamp, 1, 4) + '-' + SUBSTRING(RES.Timestamp, 5, 2) + '-' + SUBSTRING(RES.Timestamp, 7, 2) + ' ' + SUBSTRING(RES.Timestamp, 9, 2) + ':' + SUBSTRING(RES.Timestamp, 11, 2) + ':' + SUBSTRING(RES.Timestamp, 13, 2) + '.' + SUBSTRING(RES.Timestamp, 15, 3), 121)
FROM
#dtResult RES
INNER JOIN
tblA a with(nolock) ON RES.AlphaID = a.AlphaID
INNER JOIN
tblB b with(nolock) ON a.UnitId = b.UnitId AND CAST(RES.X AS INTEGER) = b.X AND CAST(RES.Y AS INTEGER) = b.Y
INNER JOIN
tblC c with(nolock) ON RES.BetaID = c.BetaID
LEFT OUTER JOIN
tblTransaction t with(nolock) ON RES.AlphaID = t.AlphaID AND RES.BetaID = t.BetaID AND t.UnitId = b.UnitId
WHERE
t.BetaID IS NULL
/* FUNCTION */
CREATE FUNCTION [dbo].[fnGetCode]
(
#Data VARCHAR(MAX),
#SearchX INT,
#SearchY INT
)
RETURNS CHAR(4)
WITH ENCRYPTION
AS
BEGIN
DECLARE #SearchResult CHAR(4)
DECLARE #Pos INT
SET #Pos = (#SearchY * #SearchX) + 1
SET #SearchResult = CONVERT(char(1),SUBSTRING(#Data,#Pos,1), 1)
RETURN #SearchResult
END

Replace specials chars with HTML entities

I have the following in table TABLE
id content
-------------------------------------
1 Hellö world, I äm text
2 ènd there äré many more chars
3 that are speçial in my dat£base
I now need to export these records into HTML files, using bcp:
set #command = 'bcp "select [content] from [TABLE] where [id] = ' +
#id queryout +' + #filename + '.html" -S ' + #instance +
' -c -U ' + #username + ' -P ' + #password"
exec xp_cmdshell #command, no_ouput
To make the output look correct, I need to first replace all special characters with their respective HTML entities (pseudo)
insert into [#temp_html] ..
replace(replace([content], 'ö', 'ö'), 'ä', 'ä')
But by now, I have 30 nested replaces and it's starting to look insane.
After much searching, I found this post which uses a HTML conversion table but it is too advanced for me to understand:
The table does not list the special chars itself as they are in my text (ö, à etc) but UnicodeHex. Do I need to add them to the table to make the conversions that I need?
I am having trouble understanding how to update my script to replace all special chars. Can someone please show me a snippet of (pseudo) code?
One way to do that with a translation table is using a recursive cte to do the replaces, and one more cte to get only the last row of each translated value.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
id int,
content nvarchar(100)
)
INSERT INTO #T (id, content) VALUES
(1, 'Hellö world, I äm text'),
(2, 'ènd there äré many more chars'),
(3, 'that are speçial in my dat£base')
Then, create and populate the translation table (I don't know the HTML entities for these chars, so I've just used numbers [plus it's easier to see in the results]). Also, please note that this can be done using yet another cte in the chain.
DECLARE #Translations AS TABLE
(
str nchar(1),
replacement nvarchar(10)
)
INSERT INTO #Translations (str, replacement) VALUES
('ö', '-1-'),
('ä', '-2-'),
('è', '-3-'),
('ä', '-4-'),
('é', '-5-'),
('ç', '-6-'),
('£', '-7-')
Now, the first cte will do the replaces, and the second cte just adds a row_number so that for each id, the last value of lvl will get 1:
;WITH CTETranslations AS
(
SELECT id, content, 1 As lvl
FROM #T
UNION ALL
SELECT id, CAST(REPLACE(content, str, replacement) as nvarchar(100)), lvl+1
FROM CTETranslations
JOIN #Translations
ON content LIKE '%' + str + '%'
), cteNumberedTranslation AS
(
SELECT id, content, ROW_NUMBER() OVER(PARTITION BY Id ORDER BY lvl DESC) rn
FROM CTETranslations
)
Select from the second cte where rn = 1, I've joined the original table to show the source and translation side by side:
SELECT r.id, s.content, r.content
FROM #T s
JOIN cteNumberedTranslation r
ON s.Id = r.Id
WHERE rn = 1
ORDER BY Id
Results:
id content content
1 Hellö world, I äm text Hell-1- world, I -4-m text
2 ènd there äré many more chars -3-nd there -4-r-5- many more chars
3 that are speçial in my dat£base that are spe-6-ial in my dat-7-base
Please note that if your content have more that 100 special chars, you will need to add the maxrecursion 0 hint to the final select:
SELECT r.id, s.content, r.content
FROM #T s
JOIN cteNumberedTranslation r
ON s.Id = r.Id
WHERE rn = 1
ORDER BY Id
OPTION ( MAXRECURSION 0 );
See a live demo on rextester.

STRING_SPLIT from column value and reverse it in SQL Server

I am trying to reverse string values in a column in SQL Server.
What I am trying to achieve is, when the value of the column is child/parent, I want to change it to parent/child when child contains '112'
For example I want to change columns with value Reports/112-Major to 112-Major/Reports
To start with, I tried to use STRING_SPLIT and append them like String_Split(ColumnName,'/')[1] + String_Split(ColumnName,'/')[0] if String_Split(ColumnName,'/')[1] like '%112%'
Most of the examples that I see online have something like
SELECT Value FROM STRING_SPLIT('Lorem/ipsum/dolor/sit/amet.', '/');
But I want to split and then merge based on condition and then update the column
Something like,
update tblTableName
set siteUrl = String_Split(ColumnName,'/')[1] + String_Split(ColumnName,'/')[0]
where `String_Split(ColumnName,'/')[1] like '%112%'
Is there a way to do this in SQL Server?
You can use this expression:
stuff(col, 1, charindex('/', col), '') + '/' + left(col, charindex('/', col) - 1)
Another option just for fun.
Gordon's solution would be my first choice and is certainly more performant (+1), but the following illustrates a simple technique which can be used to split a string into columns and not rows.
Sample
Declare #YourTable table (ID int,YourCol varchar(100))
Insert Into #YourTable values
(1,'Reports/112-Major'),
(2,'Reports/Something Else')
Update #YourTable Set YourCol = Pos2+'/'+Pos1
From #YourTable A
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
From (Select Cast('<x>' + replace((Select replace(A.YourCol,'/','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as B1
) B
Where Pos2 Like '%112%'
Updated Results
ID YourCol
1 112-Major/Reports
2 Reports/Something Else

Problems with Data Script Generation

I very rarely use SQL Server and in a professional context I keep well clear! I'm working on a pet project though and I'm have problems with a script creation.
I've got an online database that I need to extract everything out of. I use the Tasks > Generate Scripts option within SQL Server Management Studio. The following is an example of one insert statement the script creates (I have 1,000s of these inserts):
INSERT [dbo].[NewComics] ([NewComicId], [Title], [Subtitle], [ReleaseDate], [CollectionId]) VALUES (366, N'Hawk & Dove 1: ', N'First Strikes ', CAST(0x00009F6F00000000 AS DateTime), 248)
I have two issues with this:
(a) I want to strip all the whitespace from the two title elements
(b) I don't want a HEX date - I want something readable like 2006-09-01 (yyyy-mm-dd)
INSERT [dbo].[NewComics] ([NewComicId], [Title], [Subtitle], [ReleaseDate], [CollectionId]) VALUES (366, N'Hawk & Dove 1:', N'First Strikes', '2006-09-01', 248)
What would be the quickest way to change about 3,000 insert statements to this revised format?
FYI - this is the design of the table:
[NewComicId] [int] NOT NULL,
[Title] [nchar](100) NOT NULL,
[Subtitle] [nchar](100) NULL,
[ReleaseDate] [datetime] NOT NULL,
[CollectionId] [int] NOT NULL,
Thanks in advance!
Yes, generate scripts sadly scripts datetime columns as CONVERT(binary_value, Datetime). I'll try to get an answer as to why (or more importantly if there is a way to change the behavior). I suspect the reason is to avoid any issues with running the scripts on a different machine with different locale / regional settings etc. I don't know if there's a way to change that from happening in the meantime, but Management Studio isn't the only way to script your data... you could look into 3rd party products like Red-Gate's SQL Data Compare.
If it's really only 3,000 rows, and you intend to run the generated script on a different server, stop using the wizard and do this (on first glance this looks horrific, but it does several of the things you'll want - outputs a script ready to copy, paste and run, with nicely formatted and readable dates, inserts batched into multi-row VALUES by 1000 with GO commands in between, and even deals with potentially NULL values in title, subtitle and collectionid):
DECLARE #newtable SYSNAME = 'dbo.NewComics';
SET NOCOUNT ON;
;WITH x AS (SELECT TOP (4000) s = '('
+ CONVERT(VARCHAR(12), NewComicId) + ','
+ COALESCE('N''' + REPLACE(RTRIM(Title), '''', '''''') + '''', 'NULL') + ','
+ COALESCE('N''' + REPLACE(RTRIM(SubTitle), '''', '''''') + '''', 'NULL')
+ ', ''' + CONVERT(CHAR(8), ReleaseDate, 112) + ' '
+ CONVERT(CHAR(8), ReleaseDate, 108) + ''','
+ CONVERT(VARCHAR(12), COALESCE(CollectionId, 'NULL')) + ')',
rn = ROW_NUMBER() OVER (ORDER BY NewComicId)
FROM dbo.OldComics ORDER BY NewComicId
),
y AS
(
SELECT [/*a*/] = 1, [/*b*/] = 'SET NOCOUNT ON;
GO
INSERT ' + #newtable + ' VALUES'
UNION ALL
SELECT 2, s = CASE WHEN rn > 1 THEN ',' ELSE '' END + s
FROM x WHERE rn BETWEEN 1 AND 1000
UNION ALL
SELECT 3, 'GO' UNION ALL
SELECT 4, s = CASE WHEN rn > 1001 THEN ',' ELSE '' END + s
FROM x WHERE rn BETWEEN 1001 AND 2000
UNION ALL
SELECT 5, 'GO' UNION ALL
SELECT 6, s = CASE WHEN rn > 2001 THEN ',' ELSE '' END + s
FROM x WHERE rn BETWEEN 2001 AND 3000
UNION ALL
SELECT 7, 'GO' UNION ALL
SELECT 8, s = CASE WHEN rn > 3001 THEN ',' ELSE '' END + s
FROM x WHERE rn BETWEEN 3001 AND 4000
)
SELECT [/*b*/] FROM y ORDER BY [/*a*/];
(You might have to play with it if you have exactly 3000 or 3001 rows, or add another couple of unions if you have more than 4000, etc.)
If you are moving the data to a different table or different database on the same instance, use the script that #swasheck provided (and again, stop using the wizard).
You may have noticed a common trend here: stop using the generate scripts wizard if you don't like the binary format it outputs for dates.
So if this was me, what I'd do would be to build up the table structure in a separate database:
CREATE TABLE NewComics (
[NewComicId] [int] identity (0,1) NOT NULL,
[Title] [nvarchar](100) NOT NULL,
[Subtitle] [nvarchar](100) NULL,
[ReleaseDate] [datetime] NOT NULL,
[CollectionId] [int] NOT NULL
);
ALTER TABLE [NewComics]
ADD CONSTRAINT PK_NewComicsID PRIMARY KEY;
And then use SQL to clean the data like so:
INSERT INTO [NewDatabase].[dbo].[NewComics] (Title, Subtitle, ReleaseDate, CollectionID)
SELECT
LTRIM(RTRIM(Title))
, LTRIM(RTRIM(Subtitle))
, CAST(ReleaseDate as Datetime)
, CollectionID
FROM [OldDatabase].[dbo].[NewComics];
Alternatively, you can use this same SELECT query:
SELECT
NewComicID
, LTRIM(RTRIM(Title))
, LTRIM(RTRIM(Subtitle))
, CAST(ReleaseDate as Datetime)
, CollectionID
FROM [OldDatabase].[dbo].[NewComics];
as the source for an Import/Export Data Task (in the same menu that you've used to Generate Scripts). [OldDatabase] on this server would be the source and [NewDatabase] on this server would be the destination. Make sure you check the box to all identity inserts.

sql script cover all possible variable combinations? help, app, somthing?

I'm sure somebody will know of a app or some website to help do this:
I need to run a script in the 'database engine tuning advisor', but would like to do all or most of the possible combinations of variables for my select statement/function.
For example, I have:
#RegionID, can be any value from SELECT EntityGroup.Id FROM EntityGroup (e.g. 1,2,3,4)
#LanguageId, can be any value from SELECT Language.Id (e.g. en-GB, tr-TR)
#Group1, can be 1,2,3,4,5,6,7
and so on.
then to have something generate an sql script such as
SELECT * From xyz (1, 'en-GB', 1)
SELECT * From xyz (1, 'tr-TR', 1)
SELECT * From xyz (2, 'en-GB', 1)
SELECT * From xyz (2, 'tr-TR', 1)
over and over with each of the possible combinations of variables.
Any tips?
thanks
You can run this query:
SELECT 'SELECT * FROM xyz (' + CAST(A.Id AS VARCHAR(10)) + ', ''' +
B.Id + ''', ' + CAST(C.Id AS VARCHAR(10)) + ')' AS Script
FROM EntityGroup A
CROSS JOIN [Language] B
CROSS JOIN [Group] C
And then copy the results to get your script. (Though you need to know that if there are more than just those values on the tables, the CROSS JOIN will rapidly grow in size).

Resources