Ordering a list of fractions and mixed numbers - c#

I am looking for a way that would allow me to have a column that I would simply manually input a number and then the list would sort itself based off this list, this would be using c# as the language for the listing and it's ms SQL
I don't have much information, but if anyone wants to know anything else feel free to ask and I will try and answer to my best ability.
They are currently stored as strings.
The thing that causes it to get thrown out is because some of the lists contains ranges but some contain fractions, both which are displayed the same I.E 1/2 can mean 1-2 or 1/2(half)
All of the SQL connection is done using NHibernate.
The reason for not simply sorting it normally, is that the list is ordered using fractions currently and the list works fine, however when it gets over 1, it seems to break and throws all of them to the bottom of the list I.E:
Example of how I would like this to work:
I would have a column in my database named "DisplayOrder" or something along those lines.
Database row says "1", this would be the first item in a list to appear.
Database row says "8", this would be the 8th item in the list to appear.

I've put together a quick function to parse your fractions and convert them to floats, which can then be used for comparison and sorting. You may need more in the "sanitation" step, possibly normalizing whitespace, removing other characters, etc., and may have to check the divisor for zero (only you know your data).
create function dbo.FractionToFloat(#fraction varchar(100))
returns float
as
begin
declare #input varchar(100)
, #output float
, #whole int
, #dividend int
, #divisor int
-- Sanitize input
select #input = ltrim(rtrim(replace(replace(#fraction, '''', ''), '"', '')))
select #whole = cast(case
when charindex('/', #input) = 0 then #input
when charindex(' ', #input) = 0 then '0'
else left(#input, charindex(' ', #input) - 1)
end as int)
select #dividend = cast(case
when charindex('/', #input) = 0 then '0'
when charindex(' ', #input) = 0 then left(#input, charindex('/', #input) - 1)
else substring(#input, charindex(' ', #input) + 1, charindex('/', #input) - charindex(' ', #input) - 1)
end as int)
select #divisor = cast(case
when charindex('/', #input) = 0 then '1'
else right(#input, charindex('/', reverse(#input)) - 1)
end as int)
select #output = cast(#whole as float) + (cast(#dividend as float) / cast(#divisor as float))
return #output
end
This way, you can simply order by the function's output like so:
select *
from MyTable
order by dbo.FractionToFloat(MyFractionColumn)
I wouldn't normally suggest rolling your own parser that you have to maintain as the data changes, but this seems simple enough on the surface, and probably better than manually maintaining an ordinal column. Also, if you had to compare various units (feet to inches, minutes to seconds), then this gets more complicated. I'm removing anything that looks like a unit in your demo data.

The sorting result seems perfectly normal, since I really think that the database doesn't understand what "1 1/4" means.
Which type has your database field? Text/varchar/string I guess?
Maybe you should create a stored proc to convert the fraction values to numeric ones, an then call
SELECT field1, field2, ParseAndConvertProc(DisplayOrder) as disp_order ORDER by disp_order

Ignoring for the moment that 1/2 can mean between 1 and 2, you can deal with the madness and try to get some order out of it.
CREATE TABLE #Fractions (
Frac varchar(15)
);
INSERT #Fractions VALUES
('1 1/2'), ('1 1/4'), ('1"'), ('1/2'), ('1/4'), ('1/8'),
('2 1/2'), ('2"'), ('3"'), ('3/4'), ('3/8'), ('4"');
WITH Canonical AS (
SELECT
Frac,
F,
CharIndex(' ', F) S,
CharIndex('/', F) D
FROM
(SELECT Frac, Replace(Frac, '"', '') F FROM #Fractions) F
WHERE
F NOT LIKE '%[^0-9 /]%'
), Parts AS (
SELECT
Frac,
Convert(int, Left(F, CASE WHEN D = 0 THEN Len(F) ELSE S END)) W,
Convert(int, CASE WHEN D > 0 THEN Substring(F, S + 1, D - S - 1) ELSE '' END) N,
Convert(int, CASE WHEN D > 0 THEN Substring(F, D + 1, 2147483647) ELSE '1' END) D
FROM Canonical
)
SELECT
Frac,
W + N * 1.0 / D Calc
FROM Parts
ORDER BY Calc;
DROP TABLE #Fractions;
But I don't recommend this. Use my query as a base to get the correct value, and switch to using decimal values.
Note you also have to search your data for characters this code didn't account for, using the pattern match above with LIKE instead of NOT LIKE. Then remove them or account for them somehow.
If what you said is true that 1/2 can mean two things, andyou can tell the difference between them, then you can do something about it. Store the range in two columns. If they are the same then there is no range.
If you can't tell the difference between the two meanings then you're just majorly messed up and may as well quit your job and go race sled dogs in Alaska.

Related

Using T-SQL or C#, how can I determine the position and length of all substrings in a string?

Given the following string (ignore double quotes):
"I101G2.2 OZ 001 0002200 L Y 0010000 "
I need to find the starting position of each substring and it's length. For example, using the string above, the first substring is I101G2.2
starts at position 0 and is 8 characters in length. Bear in mind that each space character (" ") should be considered a substring, always one character in length. So, the output should be something like:
**Sub-String Value**,**Starting Position**, **Length**
I101G2.2,0,8
{space},9,1
OZ,10,2
{space},12,1
{space},13,1
001,14,3
{space},17,1
0002200,18,7
{space},25,1
L,26,1
{space},27,1
{space},28,1
{space},29,1
{space},30,1
{space},31,1
{space},32,1
{space},33,1
{space},34,1
Y,35,1
{space},36,1
{space},37,1
{space},38,1
{space},39,1
{space},40,1
{space},41,1
{space},42,1
{space},43,1
0010000,44,7
etc...
We have a production system written in Cobol that outputs information in the above string format. I have a table that maps positions and string lengths to a column in another table. So, the idea is to get the position and length of the string and compare to the mapping table to determine what table column the belongs in. For instance
0002200 is the item class because it's at position 18, and is 7 characters in length.
Thanks in advance!
I'm impressed you still got Cobol running, my mom used to code stuff in Cobol... In the 80s!
Here's my try (it handles all spaces even those at the end of the string):
IF object_id('tempdb..#t') IS NOT NULL
DROP TABLE #t
GO
CREATE TABLE #t (str VARCHAR(100))
INSERT INTO #t
VALUES ('I101G2.2 OZ 001 0002200 L Y 0010000')
;WITH cte AS(
SELECT cast(CASE WHEN charindex(' ', t.str) = 1 THEN ' ' ELSE LEFT(str, charindex(' ', str)) END AS varchar(100)) str
, 0 AS startPosition
, CASE WHEN charindex(' ', t.str) = 1 THEN 1 ELSE charindex(' ', t.str) END AS nextIndex
, stuff(str, 1,charindex(' ', t.str)-1, '') AS strLeft
FROM #t t
--
UNION ALL
SELECT cast(CASE WHEN charindex(' ', t.strLeft) = 1 THEN ' ' WHEN charindex(' ', t.strleft) = 0 THEN strLeft ELSE LEFT(strLeft, charindex(' ', strLeft)) END AS varchar(100))
, startPosition + nextIndex
, CASE WHEN charindex(' ', t.strLeft) = 1 THEN 1 ELSE charindex(' ', t.strLeft) END
, stuff(strLeft, 1,charindex(' ', t.strLeft) + CASE WHEN charindex(' ', t.strleft) <> 1 THEN -1 ELSE 0 END, '')
FROM CTE t
WHERE datalength(strLeft) > 0
)
SELECT str, startPosition, datalength(str) AS length
FROM cte
OPTION(maxrecursion 0);
To do this in c#, you can simply walk the string in a loop. As we encounter non-space characters, we can append them to a StringBuilder. When we encounter a space, we would then first add the characters we've captured so far, along with their length and starting position, to a list of strings (each representing a substring), clear the StringBuilder for the next substring, and then add the space character and it's position and length. At the end of the loop, if our StringBuilder has a substring in it, add it to the list and then return the list.
One bug in your original example is that the first substring started at position 0 and had 8 characters. This means that the next substring should start at position 8, but your example shows it starting at 9.
Here's one way to do it:
public static List<string> GetParts(string input)
{
if (input == null) return null;
var result = new List<string>();
if (input.Length == 0) return result;
var currentPart = new StringBuilder();
for (int i = 0; i < input.Length; i++)
{
if (input[i] == ' ')
{
if (currentPart.Length > 0)
{
var part = currentPart.ToString();
result.Add($"{part},{i - part.Length},{part.Length}");
currentPart.Clear();
}
result.Add($"{{space}},{i},1");
}
else
{
currentPart.Append(input[i]);
}
}
if (currentPart.Length > 0)
{
var part = currentPart.ToString();
result.Add($"{part},{input.Length - part.Length - 1},{part.Length}");
}
return result;
}
And an example calling it:
static void Main(string[] args)
{
var input =
"I101G2.2 OZ 001 0002200 L Y 0010000 ";
var parts = GetParts(input);
parts.ForEach(Console.WriteLine);
Console.Write("\n\nDone. Press any key to exit...");
Console.ReadKey();
}
I'm not sure of your exact requirements here, but this is a pretty standard string split with an ordinal type of situation.
Give this a whirl:
SET ANSI_PADDING ON
DECLARE #ThatsALongOne NVARCHAR(MAX) = 'I101G2.2 OZ 001 0002200 L Y 0010000 ';
DECLARE #WhaddaYaWant NVARCHAR(10) = ' ';
SET #ThatsALongOne+='.'
;WITH cte AS
(
SELECT #ThatsALongOne AS TheWholeSheBang,
CHARINDEX(#WhaddaYaWant,#ThatsALongOne)-1 AS CI, LEN(#ThatsALongOne) AS LEN,
CASE WHEN LEFT(LEFT(#ThatsALongOne ,CHARINDEX(#WhaddaYaWant,#ThatsALongOne)),1) = #WhaddaYaWant THEN #WhaddaYaWant ELSE LEFT(#ThatsALongOne ,CHARINDEX(#WhaddaYaWant,#ThatsALongOne)) END AS This,
CASE WHEN LEFT(LEFT(#ThatsALongOne ,CHARINDEX(#WhaddaYaWant,#ThatsALongOne)),1) = #WhaddaYaWant THEN RIGHT(#ThatsALongOne, LEN(#ThatsALongOne) -CHARINDEX(#WhaddaYaWant,#ThatsALongOne )) ELSE RIGHT(#ThatsALongOne, 1+ LEN(#ThatsALongOne) -CHARINDEX(#WhaddaYaWant,#ThatsALongOne )) END AS WhatsLeft
, 1 AS Incidence
, CAST(0 AS BIGINT) AS Start
UNION ALL
SELECT #ThatsALongOne AS TheWholeSheBang,
CASE WHEN LEFT(LEFT(WhatsLeft ,CHARINDEX(#WhaddaYaWant,WhatsLeft)),1) = #WhaddaYaWant THEN 1 ELSE LEN(LEFT(WhatsLeft ,CHARINDEX(#WhaddaYaWant,WhatsLeft))) END AS CI,
LEN(WhatsLeft) AS LEN,
CASE WHEN LEFT(LEFT(WhatsLeft ,CHARINDEX(#WhaddaYaWant,WhatsLeft)),1) = #WhaddaYaWant THEN #WhaddaYaWant ELSE LEFT(WhatsLeft ,CHARINDEX(#WhaddaYaWant,WhatsLeft)) END AS This,
CASE WHEN LEFT(LEFT(WhatsLeft ,CHARINDEX(#WhaddaYaWant,WhatsLeft)),1) = #WhaddaYaWant THEN RIGHT(#ThatsALongOne, LEN(WhatsLeft) -CHARINDEX(#WhaddaYaWant,WhatsLeft )) ELSE RIGHT(WhatsLeft, 1+ LEN(WhatsLeft) -CHARINDEX(#WhaddaYaWant,WhatsLeft )) END AS WhatsLeft
, Incidence + 1 AS Incidence
, CASE WHEN LEFT(LEFT(WhatsLeft ,CHARINDEX(#WhaddaYaWant,WhatsLeft)),1) = #WhaddaYaWant THEN 1 ELSE CHARINDEX(#WhaddaYaWant,WhatsLeft) END
FROM cte
WHERE WhatsLeft <> '.'
)
SELECT This AS SubString, SUM(CI) OVER (PARTITION BY cte.TheWholeSheBang ORDER BY cte.Incidence)-CI AS StartingPosition, CI AS Length
FROM cte
SubString StartingPosition Length
I101G2.2 0 8
8 1
OZ 9 2
11 1
12 1
001 13 3
..

How to sort alphanumeric column from SQL Server with C#?

I have some data stored in SQL Server like this :
A1
A2
A3
1A
2A
3A
How can sort it?
I tried this query:
select name
from Analyze_Table
group by name
order by CONVERT(INT, LEFT(name, PATINDEX('%[^0-9]%', name+'z')-1)),name
but this only sorts by the first number and after alphabetic and doesn't sort alphabetic and after number values
I've tried to sort on just the numeric portion of the name, excluding the first or last character. Then if there are 2 with the same number, they will sort again, e.g. 23 and 23A. This should give you the output you were looking for
select name
from Analyze_Table
group by name
order by case
when isnumeric(name) = 1 then cast(name as int)
when isnumeric(left(name, 1)) = 0 and isnumeric(right(name, 1)) = 0 then cast(substring(name, 2, len(name)-2) as int)
when isnumeric(left(name, 1)) = 0 then cast(right(name, len(name)-1) as int)
when isnumeric(right(name, 1)) = 0 then cast(left(name, len(name)-1) as int) end
,name
If you want the names that start with numbers first, then is this what you what?
order by (case when name like '[0-9]%' then 1 else 2 end),
name

How to sum up char code of word in T-SQL?

I need to sum char codes of last 6 symbols of string.
I can sum there last 6 characters through C# like this:
string str = "helloWorld";
var sumLastSixCharacters = str.Where((ch, i) => (i > 3) ).Sum(x => x); //631
And I've written the following code to sum up last 6 characters, but, in my view,
there should be more elegant solution:
DECLARE #Foo VARCHAR(10);
SET #Foo='helloWorld';
SELECT
CASE WHEN TRY_CONVERT(BIGINT, #Foo) IS NULL
THEN ASCII(SUBSTRING(#Foo, 5,1)) + ASCII(SUBSTRING(#Foo, 6,1)) +
ASCII(SUBSTRING(#Foo, 7,1)) + ASCII(SUBSTRING(#Foo, 8,1)) +
ASCII(SUBSTRING(#Foo, 9,1)) + ASCII(SUBSTRING(#Foo, 10,1))
ELSE 'Cast succeeded'
END AS Result;
Guys, does anybody know how can I calculate sum of last 6 characters in better way?
You might use a recursive CTE against the reversed string
DECLARE #str VARCHAR(100)='helloWorld';
WITH recCTE AS
(
SELECT REVERSE(#str) AS OriginalReversed
,1 AS Position
,SUBSTRING(REVERSE(#str),1,1) AS CharAtPos
UNION ALL
SELECT r.OriginalReversed
,r.Position +1
,SUBSTRING(r.OriginalReversed,r.Position+1,1)
FROM recCTE AS r
WHERE Position<6 --specify the character count here
)
/*
--Use this to see the intermediate result
SELECT *
FROM recCTE
*/
SELECT SUM(ASCII(CharAtPos)) --631
FROM recCTE;
Some explanation
The CTE will take the reversed input, start a 1 and take the first character (from the end).
Then it iterates down, increasing the position by one to read the next character.
This way you will transform test to
t
s
e
t
Using SUM against the ASCII values is your needed result
UPDATE
Summing up the ASCII-values might be a bad idea... any word with the same characters will lead to the same result...
If you just need to get a number out of a string you might use this:
DECLARE #str VARCHAR(100)='helloWorld';
SELECT CAST(CAST(RIGHT(#str,6) AS BINARY(6)) AS BIGINT)
This will just take the binary chunk of 6 bytes and interpret this as BIGINT
this does it. I modified it to solve the original ask.
For me, I needed a function to evaluate concatenated fields to find duplication of patterns like:
abc | cba
and
cba | abc
I can now find these instances and replace one with the other so now I'd just have
abc | cba
call it with SELECT database_of_your_choice.dbo.CREATE_STRING_CHECKSUM('yourstringhere')
--code below
use database_of_your_choice
go
CREATE FUNCTION dbo.CREATE_STRING_CHECKSUM
(
#string nvarchar(100) = NULL
)
RETURNS int
AS
BEGIN
DECLARE #pos int = 0,
#len int = len(#string),
#ascii_sum int = 0,
#iter_count int = 6
--just in case you pass a string with less than 6 characters
if(#iter_count > #len) set #iter_count=#len
--loops for #iter_count: 6 times
while #iter_count > 0 begin
--passing #len to SUBSTRING will look at the right-most char decremented
--with each iteration: get the right most 6 characters
set #ascii_sum += ASCII(substring(#string,#len,1))
set #len -= 1
set #iter_count -= 1
end
-- Return the result of the function
RETURN #ascii_sum
END
GO

How to Convert given text into the required date format in SqlServer [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I have an old database with a table with dateEntered(varchar datatype) column which can take the dates in various formats like
A> 'wk231216' which means (Week)23-(Date)12-(Year)2016
B> '231216' which means (Week)23-(Date)12-(Year)2016
C> 'wk132717' which means (week)13-(Date)27-(year)2017
Now I need to modify the above dates into this format as 'YYYY-MM-DD'
A> should become 2016-06-12(wk23 of 2016 is in June(06))
B> should become 2016-06-12
C> should become 2017-03-27
Can anyone suggest how to achieve this?
Thank You!!
Don't use a UDF, it won't scale, don't use a cursor, it won't scale either.
As a starter for ten (either there is a mistake in your examples, or I don't follow the logic 100%) you could try something like this:
DECLARE #table TABLE (naff_date VARCHAR(50));
INSERT INTO #table SELECT 'wk231216';
INSERT INTO #table SELECT '231216';
INSERT INTO #table SELECT 'wk132717';
--Preformat
WITH x AS (SELECT CASE WHEN naff_date LIKE 'wk%' THEN SUBSTRING(naff_date, 3, 50) ELSE naff_date END AS naff_date FROM #table WHERE LEN(naff_date) IN (6, 8))
SELECT
'20' + RIGHT(naff_date, 2) + '-' + RIGHT('0' + CONVERT(VARCHAR(2), MONTH(CONVERT(INT, DATEADD(WEEK, CONVERT(INT, SUBSTRING(naff_date, 1, 2)), '20170101')))), 2) + '-' + SUBSTRING(naff_date, 3, 2) AS new_date
FROM
x
WHERE
ISNUMERIC(naff_date) = 1;
There's a lots of "iffs and buts" in there, and I did my best to remove any dates that would cause an error. I would suggest trying this, but obviously adjusting the query to use your real table, then write the results somewhere else for sanity checking, hoovering up errors, etc.
I get the following results:
new_date
2016-06-12
2016-06-12
2017-04-27
So the same for the first two examples, but a month out for the other (but I think my result is correct here?).
I guess if your week was "zero based" then this would work better, it's also not assuming that each year doesn't have a leap year (as my original answer did):
DECLARE #table TABLE (naff_date VARCHAR(50));
INSERT INTO #table SELECT 'wk231216';
INSERT INTO #table SELECT '231216';
INSERT INTO #table SELECT 'wk132717';
--Preformat
WITH x AS (SELECT CASE WHEN naff_date LIKE 'wk%' THEN SUBSTRING(naff_date, 3, 50) ELSE naff_date END AS naff_date FROM #table WHERE LEN(naff_date) IN (6, 8)),
--Get the year, week and day
y AS (
SELECT
'20' + RIGHT(naff_date, 2) AS [year],
SUBSTRING(naff_date, 1, 2) AS [week],
SUBSTRING(naff_date, 3, 2) AS [day]
FROM
x
WHERE
ISNUMERIC(naff_date) = 1)
--Now we have enough information for the whole date
SELECT
[year] + '-' + RIGHT('0' + CONVERT(VARCHAR(2), MONTH(DATEADD(WEEK, CONVERT(INT, [week]) - 1, CONVERT(DATE, [year] + '0101')))), 2) + '-' + [day] AS new_date
FROM
y;
Being new to SQL I'm sure there is a lot of better ways of doing this but you could create the following function:
CREATE FUNCTION ConvertFromWeekDateToMyDate(#input VARCHAR(10))
RETURNS DateTime
AS BEGIN
declare
#Year char(4),
#Week TINYINT,
#Day TINYINT,
#month TINYINT,
#hasWk bit
set #hasWk = case when LEFT(#input, 2) = 'wk' then 1 else 0 end
set #Week = case when #hasWk = 1 then substring(#input, 3, 2) else substring(#input, 1, 2) end
set #Day = case when #hasWk = 1 then substring(#input, 5, 2) else substring(#input, 3, 2) end
set #Year = '20' + RIGHT(#input, 2)
set #month = DATEPART(MM,CAST(CONVERT(CHAR(3),
DATEADD(WW,#WEEK - 1,
CONVERT(datetime,'01/01/'+ CONVERT(char(4),#Year))),100) + ' 1900' AS DATETIME))
RETURN DATEFROMPARTS (#Year, #month, #Day)
END
The call it similar to
update YourTable set DateColumn = ConvertFromWeekDateToMyDate(DateColumn)
You can tweak the variable types, names, function names, etc.. as needed but it should get you started.
The above is using the Week-To-Month conversion code from this SO post ► Get Month from Calendar Week (SQL Server)
Testing the above Function using the following inputs:
SELECT dbo.ConvertFromWeekDateToMyDate('wk231216') as 'wk231216',
dbo.ConvertFromWeekDateToMyDate('231216') as '231216',
dbo.ConvertFromWeekDateToMyDate('wk132717') as 'wk132717'
Gave me this result (which seems to match your expected result):
wk231216 231216 wk132717
----------------------- ----------------------- -----------------------
2016-06-12 00:00:00.000 2016-06-12 00:00:00.000 2017-03-27 00:00:00.000

How hard is it to incorporate full text search with SQL Server?

I am building a C#/ASP.NET app with an SQL backend. I am on deadline and finishing up my pages, out of left field one of my designers incorporated a full text search on one of my pages. My "searches" up until this point have been filters, being able to narrow a result set by certain factors and column values.
Being that I'm on deadline (you know 3 hours sleep a night, at the point where I am looking like something the cat ate and threw up), I was expecting this page to be very similar to be others and I'm trying to decide whether or not to make a stink. I have never done a full text search on a page before.... is this a mountain to climb or is there a simple solution?
thank you.
First off, you need to enabled Full text Searching indexing on the production servers, so if thats not in scope, your not going to want to go with this.
However, if that's already ready to go, full text searching is relatively simple.
T-SQL has 4 predicates used for full text search:
FREETEXT
FREETEXTTABLE
CONTAINS
CONTAINSTABLE
FREETEXT is the simplest, and can be done like this:
SELECT UserName
FROM Tbl_Users
WHERE FREETEXT (UserName, 'bob' )
Results:
JimBob
Little Bobby Tables
FREETEXTTABLE works the same as FreeTEXT, except it returns the results as a table.
The real power of T-SQL's full text search comes from the CONTAINS (and CONTAINSTABLE) predicate...This one is huge, so I'll just paste its usage in:
CONTAINS
( { column | * } , '< contains_search_condition >'
)
< contains_search_condition > ::=
{ < simple_term >
| < prefix_term >
| < generation_term >
| < proximity_term >
| < weighted_term >
}
| { ( < contains_search_condition > )
{ AND | AND NOT | OR } < contains_search_condition > [ ...n ]
}
< simple_term > ::=
word | " phrase "
< prefix term > ::=
{ "word * " | "phrase * " }
< generation_term > ::=
FORMSOF ( INFLECTIONAL , < simple_term > [ ,...n ] )
< proximity_term > ::=
{ < simple_term > | < prefix_term > }
{ { NEAR | ~ } { < simple_term > | < prefix_term > } } [ ...n ]
< weighted_term > ::=
ISABOUT
( { {
< simple_term >
| < prefix_term >
| < generation_term >
| < proximity_term >
}
[ WEIGHT ( weight_value ) ]
} [ ,...n ]
)
This means you can write queries such as:
SELECT UserName
FROM Tbl_Users
WHERE CONTAINS(UserName, '"little*" NEAR tables')
Results:
Little Bobby Tables
Good luck :)
Full text search in SQL Server is really easy, a bit of configuration and a slight tweak on the queryside and you are good to go! I have done it for clients in under 20 minutes before, being familiar with the process
Here is the 2008 MSDN article, links go out to the 2005 versions from there
I have used dtSearch before for adding full text searching to files and databases, and their stuff is pretty cheap and easy to use.
Short of adding all that and configuring SQL, This script will search through all columns in a database and tell you what columns contain the values you are looking for. I know its not the "proper" solution, but may buy you some time.
/*This script will find any text value in the database*/
/*Output will be directed to the Messages window. Don't forget to look there!!!*/
SET NOCOUNT ON
DECLARE #valuetosearchfor varchar(128), #objectOwner varchar(64)
SET #valuetosearchfor = '%staff%' --should be formatted as a like search
SET #objectOwner = 'dbo'
DECLARE #potentialcolumns TABLE (id int IDENTITY, sql varchar(4000))
INSERT INTO #potentialcolumns (sql)
SELECT
('if exists (select 1 from [' +
[tabs].[table_schema] + '].[' +
[tabs].[table_name] +
'] (NOLOCK) where [' +
[cols].[column_name] +
'] like ''' + #valuetosearchfor + ''' ) print ''SELECT * FROM [' +
[tabs].[table_schema] + '].[' +
[tabs].[table_name] +
'] (NOLOCK) WHERE [' +
[cols].[column_name] +
'] LIKE ''''' + #valuetosearchfor + '''''' +
'''') as 'sql'
FROM information_schema.columns cols
INNER JOIN information_schema.tables tabs
ON cols.TABLE_CATALOG = tabs.TABLE_CATALOG
AND cols.TABLE_SCHEMA = tabs.TABLE_SCHEMA
AND cols.TABLE_NAME = tabs.TABLE_NAME
WHERE cols.data_type IN ('char', 'varchar', 'nvchar', 'nvarchar','text','ntext')
AND tabs.table_schema = #objectOwner
AND tabs.TABLE_TYPE = 'BASE TABLE'
ORDER BY tabs.table_catalog, tabs.table_name, cols.ordinal_position
DECLARE #count int
SET #count = (SELECT MAX(id) FROM #potentialcolumns)
PRINT 'Found ' + CAST(#count as varchar) + ' potential columns.'
PRINT 'Beginning scan...'
PRINT ''
PRINT 'These columns contain the values being searched for...'
PRINT ''
DECLARE #iterator int, #sql varchar(4000)
SET #iterator = 1
WHILE #iterator <= (SELECT Max(id) FROM #potentialcolumns)
BEGIN
SET #sql = (SELECT [sql] FROM #potentialcolumns where [id] = #iterator)
IF (#sql IS NOT NULL) and (RTRIM(LTRIM(#sql)) <> '')
BEGIN
--SELECT #sql --use when checking sql output
EXEC (#sql)
END
SET #iterator = #iterator + 1
END
PRINT ''
PRINT 'Scan completed'
I've been there. It works like a charm until you start to consider scalability and advanced search functionalities like search over multiple columns with giving each one different weight values.
For example, the only way to search over Title and Summary columns is to have a computed column with SearchColumn = CONCAT(Title, Summary) and index over SearchColumn. Weighting? SearchColumn = CONCAT(CONCAT(Title,Title), Summary) something like that. ;) Filtering? Forget about it.
"How hard is it" is a tough question to answer. For example, someone who's already done it 10 times will probably reckon it's a snap. All I can really say is that you're likely to find it a lot easier if you use something like NLucene rather than rolling your own.

Categories