Related
DECLARE #SchemaName VARCHAR(100)
DECLARE #TableName VARCHAR(256)
DECLARE #IndexName VARCHAR(256)
DECLARE #ColumnName VARCHAR(100)
DECLARE #is_unique VARCHAR(100)
DECLARE #IndexTypeDesc VARCHAR(100)
DECLARE #FileGroupName VARCHAR(100)
DECLARE #is_disabled VARCHAR(100)
DECLARE #IndexOptions VARCHAR(MAX)
DECLARE #IndexColumnId INT
DECLARE #IsDescendingKey INT
DECLARE #IsIncludedColumn INT
DECLARE #TSQLScripCreationIndex VARCHAR(MAX)
DECLARE #TSQLScripDisableIndex VARCHAR(MAX)
DECLARE CursorIndex CURSOR
FOR
SELECT SCHEMA_NAME(t.schema_id) [schema_name] ,
t.name ,
ix.name ,
CASE WHEN ix.is_unique = 1 THEN 'UNIQUE '
ELSE ''
END ,
ix.type_desc ,
CASE WHEN ix.is_padded = 1 THEN 'PAD_INDEX = ON, '
ELSE 'PAD_INDEX = OFF, '
END
+ CASE WHEN ix.allow_page_locks = 1 THEN 'ALLOW_PAGE_LOCKS = ON, '
ELSE 'ALLOW_PAGE_LOCKS = OFF, '
END
+ CASE WHEN ix.allow_row_locks = 1 THEN 'ALLOW_ROW_LOCKS = ON, '
ELSE 'ALLOW_ROW_LOCKS = OFF, '
END
+ CASE WHEN INDEXPROPERTY(t.object_id, ix.name, 'IsStatistics') = 1
THEN 'STATISTICS_NORECOMPUTE = ON, '
ELSE 'STATISTICS_NORECOMPUTE = OFF, '
END
+ CASE WHEN ix.ignore_dup_key = 1 THEN 'IGNORE_DUP_KEY = ON, '
ELSE 'IGNORE_DUP_KEY = OFF, '
END + 'SORT_IN_TEMPDB = OFF,DROP_EXISTING = ON' AS IndexOptions ,
ix.is_disabled ,
FILEGROUP_NAME(ix.data_space_id) FileGroupName
FROM sys.tables t
INNER JOIN sys.indexes ix ON t.object_id = ix.object_id
WHERE ix.type > 0
AND ix.is_primary_key = 0
AND ix.is_unique_constraint = 0 --and schema_name(tb.schema_id)= #SchemaName and tb.name=#TableName
AND t.is_ms_shipped = 0
AND t.name <> 'sysdiagrams'
AND t.name = 'LegalEntity'
ORDER BY SCHEMA_NAME(t.schema_id) ,
t.name ,
ix.name
OPEN CursorIndex
FETCH NEXT FROM CursorIndex INTO #SchemaName, #TableName, #IndexName,
#is_unique, #IndexTypeDesc, #IndexOptions, #is_disabled, #FileGroupName
WHILE ( ##fetch_status = 0 )
BEGIN
DECLARE #IndexColumns VARCHAR(MAX)
DECLARE #IncludedColumns VARCHAR(MAX)
SET #IndexColumns = ''
SET #IncludedColumns = ''
DECLARE CursorIndexColumn CURSOR
FOR
SELECT col.name ,
ixc.is_descending_key ,
ixc.is_included_column
FROM sys.tables tb
INNER JOIN sys.indexes ix ON tb.object_id = ix.object_id
INNER JOIN sys.index_columns ixc ON ix.object_id = ixc.object_id
AND ix.index_id = ixc.index_id
INNER JOIN sys.columns col ON ixc.object_id = col.object_id
AND ixc.column_id = col.column_id
WHERE ix.type > 0
AND ( ix.is_primary_key = 0
OR ix.is_unique_constraint = 0
)
AND SCHEMA_NAME(tb.schema_id) = #SchemaName
AND tb.name = #TableName
AND ix.name = #IndexName
ORDER BY ixc.index_column_id
OPEN CursorIndexColumn
FETCH NEXT FROM CursorIndexColumn INTO #ColumnName, #IsDescendingKey,
#IsIncludedColumn
WHILE ( ##fetch_status = 0 )
BEGIN
IF #IsIncludedColumn = 0
SET #IndexColumns = #IndexColumns + #ColumnName
+ CASE WHEN #IsDescendingKey = 1 THEN ' DESC, '
ELSE ' ASC, '
END
ELSE
SET #IncludedColumns = #IncludedColumns + #ColumnName
+ ', '
FETCH NEXT FROM CursorIndexColumn INTO #ColumnName,
#IsDescendingKey, #IsIncludedColumn
END
CLOSE CursorIndexColumn
DEALLOCATE CursorIndexColumn
SET #IndexColumns = SUBSTRING(#IndexColumns, 1, LEN(#IndexColumns) - 1)
SET #IncludedColumns = CASE WHEN LEN(#IncludedColumns) > 0
THEN SUBSTRING(#IncludedColumns, 1,
LEN(#IncludedColumns) - 1)
ELSE ''
END
-- print #IndexColumns
-- print #IncludedColumns
SET #TSQLScripCreationIndex = ''
SET #TSQLScripDisableIndex = ''
SET #TSQLScripCreationIndex = 'CREATE ' + #is_unique + #IndexTypeDesc
+ ' INDEX ' + QUOTENAME(#IndexName) + ' ON '
+ QUOTENAME(#SchemaName) + '.' + QUOTENAME(#TableName) + '('
+ #IndexColumns + ') '
+ CASE WHEN LEN(#IncludedColumns) > 0
THEN CHAR(13) + 'INCLUDE (' + #IncludedColumns + ')'
ELSE ''
END + CHAR(13) + 'WITH (' + #IndexOptions + ') ON '
+ '[SC_LE]([Id])' + ';'
IF #is_disabled = 1
SET #TSQLScripDisableIndex = CHAR(13) + 'ALTER INDEX '
+ QUOTENAME(#IndexName) + ' ON ' + QUOTENAME(#SchemaName)
+ '.' + QUOTENAME(#TableName) + ' DISABLE;' + CHAR(13)
--print #TSQLScripCreationIndex
--print #TSQLScripDisableIndex
FETCH NEXT FROM CursorIndex INTO #SchemaName, #TableName, #IndexName,
#is_unique, #IndexTypeDesc, #IndexOptions, #is_disabled,
#FileGroupName
END
CLOSE CursorIndex
DEALLOCATE CursorIndex
The issue here is probably where you call FILEGROUP_NAME() in the SELECT clause of the definition of CursorIndex. As per the MSDN definition, the filegroup_id parameter passed to FILEGROUP_NAME() is of type smallint. I expect you have one or more records in sys.indexes which have a data_space_id value greater than 32,767 (the maximum value which can be stored in a smallint variable).
You can check by running:
SELECT SCHEMA_NAME(t.schema_id) [schema_name] ,
t.name AS TableName,
ix.name AS IndexName,
ix.data_space_id
FROM sys.tables t
INNER JOIN sys.indexes ix ON t.object_id = ix.object_id
WHERE ix.type > 0
AND ix.is_primary_key = 0
AND ix.is_unique_constraint = 0
AND t.is_ms_shipped = 0
AND ix.data_space_id > 32767
ORDER BY SCHEMA_NAME(t.schema_id),
t.name,
ix.name
This will return any indexes with this problem. I can see two ways to fix your query:
Omit the FILEGROUP_NAME() call from the original cursor definition and subsequent FETCH calls - it doesn't look like you use it for anything later on, so there's no need for it to be included.
Amend the WHERE clause of the original cursor definition to exclude any indexes with data_space_id values greater than 32767. Note that this approach will mean some indexes are not included in your cursor, but these will only be the ones which have been causing it to fail anyway.
As for why SQL Server allows values greater than 32767 in sys.indexes.data_space_id while FILEGROUP_NAME() requires a smallint parameter, I have no idea - maybe someone else can enlighten us on this?
How to generate a matrix of random numbers where the values in each row add up to X in T-SQL?
The solution matrix should be dynamic:
User can specify number of columns to be returned in the result
User can specify number of rows to be returned in the result
Each row must sum to X (eg. 1)
create proc RandomNumberGenerator
(
#rows int
, #cols int
, #rowsumtotal float
)
as
....
First create a UDF...
CREATE FUNCTION [dbo].[_ex_fn_SplitToTable] (#str varchar(5000), #sep varchar(1) = null)
RETURNS #ReturnVal table (n int, s varchar(5000))
AS
/*
Alpha Test
-----------
select * from [dbo].[_ex_fn_SplitToTable_t2]('a b c d e',' ')
*/
BEGIN
if #sep = ' '
begin
set #sep = CHAR(167)
set #str = REPLACE(#str,' ',CHAR(167))
end
declare #str2 varchar(5000)
declare #sep2 varchar(1)
if LEN(ISNULL(#sep,'')) = 0
begin
declare #i int
set #i = 0
set #str2 = ''
declare #char varchar(1)
startloop:
set #i += 1
--print #i
set #char = substring(#str,#i,1)
set #str2 = #str2 + #char + ','
if LEN(#str) <= #i
goto exitloop
goto startloop
exitloop:
set #str2 = left(#str2,LEN(#str2) - 1)
set #sep2 = ','
--print #str2
end
else
begin
set #str2 = #str
set #sep2 = #sep
end
;WITH Pieces(n, start, stop) AS (
SELECT 1, 1, CHARINDEX(#sep2, #str2)
UNION ALL
SELECT n + 1, stop + 1, CHARINDEX(#sep2, #str2, stop + 1)
FROM Pieces
WHERE stop > 0
)
insert into #ReturnVal(n,s)
SELECT n,
SUBSTRING(#str2, start, CASE WHEN stop > 0 THEN stop-start ELSE 5000 END) AS s
FROM Pieces option (maxrecursion 32767)
RETURN
END
GO
Then, create this stored proc...
CREATE PROC [dbo].[RandomNumberGenerator]
(
#Pockets int = 6,
#SumTo float = 100,
#i_iterations int = 100
)
/*
ALPHA TEST
----------
exec RandomNumberGenerator 10, 100, 500
*/
AS
if object_id('tempdb..#_Random_00') is not null drop table #_Random_00
declare #columnstring varchar(max) = (SELECT REPLICATE('c ',#Pockets) as Underline)
print #columnstring
if object_id('tempdb..#_Random_columns') is not null drop table #_Random_columns
select s+CONVERT(varchar,dbo.PadLeft(convert(varchar,n),'0',3)) cols
into #_Random_columns
from [dbo].[_ex_fn_SplitToTable](#columnstring,' ') where LEN(s) > 0
-- ===========================
--select * from #_Random_columns
-- ===========================
declare #columns_sql varchar(max)
set #columns_sql =
(
select distinct
stuff((SELECT distinct + cast(cols as varchar(50)) + ' float, '
FROM (
select cols
from #_Random_columns
) t2
--where t2.n = t1.n
FOR XML PATH('')),3,0,'')
from (
select cols
from #_Random_columns
) t1
)
set #columns_sql = LEFT(#columns_sql,LEN(#columns_sql) - 1)
print #columns_sql
declare #sql varchar(max)
set #sql = 'if object_id(''tempdb..##_proctable_Random_01'') is not null drop table ##_proctable_Random_01 '
print #sql
execute(#sql)
set #sql = 'create table ##_proctable_Random_01 (rowid int,' + #columns_sql + ')'
print #sql
execute(#sql)
declare #TotalOfRand float
declare #i_inner int
declare #i_outer int
set #i_outer = 0
start_outer:
set #i_outer = #i_outer + 1
set #i_inner = 0
declare #sumstring varchar(max)
set #sumstring = ''
start_inner:
set #i_inner = #i_inner+1
set #sumstring = #sumstring + CONVERT(varchar, rand()) + ','
if #i_inner >= #Pockets
goto exit_inner
goto start_inner
exit_inner:
set #TotalOfRand = ( select sum(convert(float,s)) from dbo._ex_fn_SplitToTable(#sumstring,',') )
declare #sumstring_quotient varchar(max)
set #sumstring_quotient = replace(#sumstring,',', '/' + Convert(varchar,#TotalOfRand) + '*' + convert(varchar,#SumTo) + ',')
set #sumstring_quotient = LEFT(#sumstring_quotient,len(#sumstring_quotient) - 1)
print #sumstring_quotient
set #sql = '
insert into ##_proctable_Random_01
select
( select count(*) + 1 from ##_proctable_Random_01 ) rowid,' + #sumstring_quotient
execute(#sql)
if #i_outer >= #i_iterations
goto exit_outer
goto start_outer
exit_outer:
select * from ##_proctable_Random_01
drop table ##_proctable_Random_01
GO
I have written the query and it is working:
declare #word as nvarchar (20)
set #word = 'victOR aALEXander'
select upper(left(#word, 1)) + lower(SUBSTRING(#word,2,charindex(' ', #word)-2)) + ' ' +
upper(left(substring(#word,charindex(' ', #word)+1,len(#word)-1),1))
+ lower(SUBSTRING(#word,charindex(' ', #word)+2, len(#word)))
I have created the function:
alter function letters ( #word as nvarchar(20))
returns varchar(20) as begin
return upper(left(#word, 1)) + lower(SUBSTRING(#word,2,charindex(' ', #word)-2)) + ' ' +
upper(left(substring(#word,charindex(' ', #word)+1,len(#word)-1),1))
+ lower(SUBSTRING(#word,charindex(' ', #word)+2, len(#word))) end
Finally i have done:
select dbo.letters(users)
from dbo.tempdb
I have got:
Invalid length parameter passed to the SUBSTRING function
Why?
Try this function:
Create Function dbo.Proper(#Data VarChar(8000))
Returns VarChar(8000)
As
Begin
Declare #Position Int
Select #Data = Stuff(Lower(#Data), 1, 1, Upper(Left(#Data, 1))),
#Position = PatIndex('%[^a-zA-Z][a-z]%', #Data COLLATE Latin1_General_Bin)
While #Position > 0
Select #Data = Stuff(#Data, #Position, 2, Upper(SubString(#Data, #Position, 2))),
#Position = PatIndex('%[^a-zA-Z][a-z]%', #Data COLLATE Latin1_General_Bin)
Return #Data
End
This function works properly whether there are spaces, apostrophes, or anything else in your data. Unfortunately it won't convert macdonald to MacDonald or o'brien to O'Brien. However, it will work for any word(s) that only have 1 capital letter in it.
also try trimming in case you have spaces at the beginning and end:
alter function letters ( #word as nvarchar(20))
returns varchar(20) as
begin
set #word = ltrim(rtrim(#word))
return upper(left(#word, 1)) + lower(SUBSTRING(#word,2,charindex(' ', #word)-2)) + ' ' +
upper(left(substring(#word,charindex(' ', #word)+1,len(#word)-1),1))
+ lower(SUBSTRING(#word,charindex(' ', #word)+2, len(#word)))
end
Character count of #word parameter is less than substring count. Also, parameter could be null.
try if this query works. However if you have more than single ' ' then this query will fail.
declare #word as nvarchar (20)
set #word = 'alex' -- 'victOR aALEXander'
select
upper(left(#word, 1)) +
CASE WHEN charindex(' ', #word)>0 THEN lower(SUBSTRING(#word,2,charindex(' ', #word)-2)) + ' '
+ upper(left(substring(#word,charindex(' ', #word)+1,len(#word)-1),1))
+ lower(SUBSTRING(#word,charindex(' ', #word)+2, len(#word)))
ELSE lower(SUBSTRING(#word,2,len(#word)-1)) END
I have a database column set to char(255) (yes, CHAR. Don't ask me why that's how the database was set up) that at present has an empty string with two spaces (i.e. " "). Using NULLIF(LTRIM(RTRIM(column_name)), '') does NOT work (the output is [two empty spaces]). However, using NULLIF(' ', '') works correctly and the output is NULL. In other words, the actual column value works correctly, while passing the name of the column returns an incorrect value.
Any ideas on this?
I believe the column must have more than just spaces. For example:
CREATE TABLE #x(id INT, y CHAR(255));
INSERT #X SELECT 1, ' '
UNION ALL SELECT 2, ' '
UNION ALL SELECT 3, ' ' + CHAR(9);
SELECT id, NULLIF(LTRIM(RTRIM(y)),'') FROM #x;
Results:
1 NULL
2 NULL
3
For a row where this fails, try this:
DECLARE #s CHAR(255);
SELECT #s = y FROM #x WHERE id = 3;
DECLARE #i INT;
SET #i = 1;
WHILE #i <= DATALENGTH(#s)
BEGIN
IF ASCII(SUBSTRING(#s, #i, 1)) <> 32
BEGIN
PRINT 'Position ' + RTRIM(#i) + ' = CHAR('
+ RTRIM(ASCII(SUBSTRING(#s, #i, 1))) + ')';
END
SET #i = #i + 1;
END
It should tell you what other characters are in there, and where.
The question is self explanatory. Could you please point out a way to put spaces between each capital letter of a string.
SELECT dbo.SpaceBeforeCap('ThisIsATestString')
would result in
This Is A Test String.
This will add spaces only if the previous and next character is lowercase. That way 'MyABCAnalysis' will be 'My ABC Analysis'.
I added a check for a previous space too. Since some of our strings are prefixed with 'GR_' and some also contain underscores, we can use the replace function as follows:
select dbo.GR_SpaceBeforeCap(replace('GR_ABCAnalysis_Test','_',' '))
Returns 'GR ABC Analysis Test'
CREATE FUNCTION GR_SpaceBeforeCap (
#str nvarchar(max)
)
returns nvarchar(max)
as
begin
declare
#i int, #j int
, #cp nchar, #c0 nchar, #c1 nchar
, #result nvarchar(max)
select
#i = 1
, #j = len(#str)
, #result = ''
while #i <= #j
begin
select
#cp = substring(#str,#i-1,1)
, #c0 = substring(#str,#i+0,1)
, #c1 = substring(#str,#i+1,1)
if #c0 = UPPER(#c0) collate Latin1_General_CS_AS
begin
-- Add space if Current is UPPER
-- and either Previous or Next is lower
-- and Previous or Current is not already a space
if #c0 = UPPER(#c0) collate Latin1_General_CS_AS
and (
#cp <> UPPER(#cp) collate Latin1_General_CS_AS
or #c1 <> UPPER(#c1) collate Latin1_General_CS_AS
)
and #cp <> ' '
and #c0 <> ' '
set #result = #result + ' '
end -- if #co
set #result = #result + #c0
set #i = #i + 1
end -- while
return #result
end
Assuming SQL Server 2005 or later, this modified from code taken here: http://www.kodyaz.com/articles/case-sensitive-sql-split-function.aspx
CREATE FUNCTION SpaceBeforeCap
(
#str nvarchar(max)
)
returns nvarchar(max)
as
begin
declare #i int, #j int
declare #returnval nvarchar(max)
set #returnval = ''
select #i = 1, #j = len(#str)
declare #w nvarchar(max)
while #i <= #j
begin
if substring(#str,#i,1) = UPPER(substring(#str,#i,1)) collate Latin1_General_CS_AS
begin
if #w is not null
set #returnval = #returnval + ' ' + #w
set #w = substring(#str,#i,1)
end
else
set #w = #w + substring(#str,#i,1)
set #i = #i + 1
end
if #w is not null
set #returnval = #returnval + ' ' + #w
return ltrim(#returnval)
end
This can then be called just as you have suggested above.
This function combines previous answers. Selectively choose to preserve adjacent CAPS:
CREATE FUNCTION SpaceBeforeCap (
#InputString NVARCHAR(MAX),
#PreserveAdjacentCaps BIT
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE
#i INT, #j INT,
#previous NCHAR, #current NCHAR, #next NCHAR,
#result NVARCHAR(MAX)
SELECT
#i = 1,
#j = LEN(#InputString),
#result = ''
WHILE #i <= #j
BEGIN
SELECT
#previous = SUBSTRING(#InputString,#i-1,1),
#current = SUBSTRING(#InputString,#i+0,1),
#next = SUBSTRING(#InputString,#i+1,1)
IF #current = UPPER(#current) COLLATE Latin1_General_CS_AS
BEGIN
-- Add space if Current is UPPER
-- and either Previous or Next is lower or user chose not to preserve adjacent caps
-- and Previous or Current is not already a space
IF #current = UPPER(#current) COLLATE Latin1_General_CS_AS
AND (
#previous <> UPPER(#previous) COLLATE Latin1_General_CS_AS
OR #next <> UPPER(#next) collate Latin1_General_CS_AS
OR #PreserveAdjacentCaps = 0
)
AND #previous <> ' '
AND #current <> ' '
SET #result = #result + ' '
END
SET #result = #result + #current
SET #i = #i + 1
END
RETURN #result
END
GO
SELECT dbo.SpaceBeforeCap('ThisIsASampleDBString', 1)
GO
SELECT dbo.SpaceBeforeCap('ThisIsASampleDBString', 0)
CLR and regular expressions or 26 replace statements a case sensitive collate clause and a trim.
Another strategy would be to check the ascii value of each character:
create function SpaceBeforeCap
(#str nvarchar(max))
returns nvarchar(max)
as
begin
declare #result nvarchar(max)= left(#str, 1),
#i int = 2
while #i <= len(#str)
begin
if ascii(substring(#str, #i, 1)) between 65 and 90
select #result += ' '
select #result += substring(#str, #i, 1)
select #i += 1
end
return #result
end
/***
SELECT dbo.SpaceBeforeCap('ThisIsATestString')
**/
To avoid loops altogether, use of a tally table can help here. If you are running on SQL 2022, then the generate_series function can remove even this dependency. This method will be significantly faster than iterating through a loop.
create function core.ufnAddSpaceBeforeCapital
(
#inputString nvarchar(max)
)
returns nvarchar(max)
as
begin
declare #outputString nvarchar(max)
select
#outputString = string_agg(iif(t.value = 1, upper(substring(#inputString,t.value,1)),iif(ascii(substring(#inputString,t.value,1)) between 65 and 90, ' ','') + substring(#inputString,t.value,1)),'')
from
generate_series(1,cast(len(#inputString) as int)) t
return #outputString
end
The scalar function is not inlineable, so I've provided an alternative inline table-valued function if that's what you need.
create function core.ufnAddSpaceBeforeCapitalITVF
(
#inputString nvarchar(max)
)
returns table
as
return
(
select
string_agg(iif(t.value = 1, upper(substring(#inputString,t.value,1)),iif(ascii(substring(#inputString,t.value,1)) between 65 and 90, ' ','') + substring(#inputString,t.value,1)),'') as outputString
from
generate_series(1,cast(len(#inputString) as int)) t
)
end
While I really like the char looping answers I was not thrilled with the performance. I have found this performs in a fraction of the time for my use case.
CREATE function SpaceBeforeCap
(#examine nvarchar(max))
returns nvarchar(max)
as
begin
DECLARE #index as INT
SET #index = PatIndex( '%[^ ][A-Z]%', #examine COLLATE Latin1_General_BIN)
WHILE #index > 0 BEGIN
SET #examine = SUBSTRING(#examine, 1, #index) + ' ' + SUBSTRING(#examine, #index + 1, LEN(#examine))
SET #index = PatIndex( '%[^ ][A-Z]%', #examine COLLATE Latin1_General_BIN)
END
RETURN LTRIM(#examine)
end
This makes use of the fact that
case sensitive pattern search only works in some collations. The character class [^ ] means anything except space, so as we add the missing spaces we match farther into the string until it is complete.