Putting a full-text search index on a view with a function - sql-server

I'm using SQL Server 2008 and I have a view that looks like this:
select ID, dbo.functionname(ID) from tablename
I'm trying to put a full-text index on this, but it doesn't seem to have a unique index that I can go off of. The tablename.ID is the unique identifier.
I tried creating an index on it, but it says it cannot schema bind view because the function is not schemabound.
What do I need to do to get the full text index created?

To be able to create an index view, the view must be deterministic, that is it must be the guarantied to be the same on every query of it.
Is your userfunction deterministic?
User-Defined Function Determinism
Whether a user-defined function is
deterministic or nondeterministic
depends on how the function is coded.
User-defined functions are
deterministic if:
* The function is schema-bound.
* All built-in or user-defined functions called by the user-defined
function are deterministic.
* The body of the function references no database objects outside
the scope of the function. For
example, a deterministic function
cannot reference tables other than
table variables that are local to the
function.
* The function does not call any extended stored procedures.
User-defined functions that do not
meet these criteria are marked as
nondeterministic. Built-in
nondeterministic functions are not
allowed in the body of user-defined
functions.
Is your function SchemaBound?
alter function [dbo].[UserFunction]
(#example int = 1 )
returns int
with schemabinding
as
begin
return 1
end
Is your view SchemaBound?
ALTER VIEW dbo.UserView
WITH SCHEMABINDING
AS
SELECT ID, [dbo].userFunction
To create an indexed view you must first create a unique clustered index(See FAQ a bottom).

I presume you mean't "not" schemabound in the comments above
Recreate your view "WITH SCHEMABINDING"
Then create a unique clusterred index
as explained here http://www.mssqltips.com/tip.asp?tip=1610
Then try adding your Full Text Index

Related

WHERE clause partly based on a field value from another table: is there a better way than exec with a dynamic string?

I have a table with about 50,000 records (a global index of corporate and government bonds).
I would like the user to be able to filter this master index firstly into a smaller subset index (based on permanent logic), and then apply further run time criteria that vary each time.
For example, let's say the user wanted to start from one of many subset indices of bonds, let's say of government bonds only, rather than government and corporate bonds, and also only wanted the US$ government bond index specifically. This would be a permanently defined subset of the master index, with a where clause something like "[Level1]='Government' AND [Currency]='USD' AND [CountryCode]='US'"
At run time, the user would additionally request additional criteria, say for example "AND [IssueSize] > 1,000,000,000 AND [Yield] > 0.0112".
I initially thought of having a separate table that stored the different criteria for these permanent sub-indices as where clauses, for example it might have columns "IndexCode, IndexLogic", and using the example above the values would be "UST", "[Level1]='Government' and [Currency]='USD' AND [CountryCode]='US'", and there would be dozens of rows in this table defining commonly used bond indices.
I had originally thought of creating a dynamic string at run-time, where the user supplies their choice of sub-index code ('UST' in the example above), which then adds the relevant where conditions, and any additional criteria passed as separate parameters, and then doing an exec(#tsql) type command. I had also thought of perhaps having a where clause that was a function call, but this seems very inefficient?
Is the dynamic string method the best way of doing this, or is there a better way involving some kind of 'eval' function equivalent which can take a field value and use that as a where clause?
The problem here is you don't know in advance what the filtered index is.
A solution I have used in this instance, where the filtered index can often change is to grab the definition of the filter back into the client app, and use that to dynamically generate the SQL batch. You can also do this with dynamic SQL in a stored procedure:
SELECT ISNULL(
(SELECT i.filter_definition
FROM sys.indexes i
WHERE i.object_id = OBJECT_ID(#tablename) AND
i.name = #indexname AND has_filter = 1),
'(1=1)');
You pass the table name, and the index name, and you get back the exact definition for the index. This has the benefit of if the index is dropped, the condition becomes (1=1) i.e. every row. You can change this to (1=0) to return nothing instead.
Then you concat this into your dynamic query like so:
SELECT *
FROM table
WHERE regular_conditions_here
AND concated_filter_here
If you are joining other tables, I would advise you to subquery the filter, otherwise you many get column clash as there are no aliases.
SELECT *
FROM (SELECT * FROM table WHERE concated_filter_here) table
JOIN othertables etc
WHERE regular_conditions_here

Common TVF in SQL Server to get results from different schema

I have been using SQL Server for the past month and I need a suggestion from SQL Server folks to help me on this use case.
The tables below are just to explain about the idea that I am looking for.
I have tables in different schema like this:
MyDb.dbo.Festivals
MyDb.India.Festivals
MyDb.China.Festivals
MyDb.USA.Festivals
I am writing a table value function without any schema prefixed in it like
CREATE FUNCTION getFestivals()
RETURNS TABLE
AS
RETURN
(SELECT * FROM festivals)
As I haven't applied any schema, it defaults to dbo and creates the TVF as dbo.getFestivals(). Now I have created synonyms for all other schemas
CREATE SYNONYM India.getFestivals FOR dbo.getFestivals;
CREATE SYNONYM USA.getFestivals FOR dbo.getFestivals;
I tried to query like
SELECT *
FROM MyDb.India.getFestivals()
and it returns the festivals from dbo.festivals and not india.festivals.
I understand that though the synonyms, we've created it just executes the select query in the dbo schema context and not in india schema context.
I want suggestions on how to have a common table value function that will query based on the schema prefixed, i.e. MyDB.India.getFestivals() should get festivals from India and MyDB.USA.getFestivals() should return festivals from USA.
Question
Is there a way I can have a table value function that can query based on the schema context.
the only possible way I can think of is to create the same TableValue function in all schemas
Caveats
I have to stick to table value function only and the above use case is a sample scenario to explain my problem
I understand that though the synonyms, we've created it just executes
the select query in the dbo schema context and not in india schema
context.
You should always schema qualify objects in your queries, since you did not do it, SQL Server first looks for festivals in the same schema where the procedure resides, if it's not found then dbo schema is checked, if it's not found even in dbo, the error is raised.
In your case procedure resides in dbo schema so only dbo schema is checked in order to find festivals.
It may be wrong design if many "similar" tables are created instead of one table, can you merge them all into one table adding country_id to distinguish the country?
If not, can you at least add this field to every table? If it's so, just add the field for the country in every table, add check constraint on this field to reflect the only country that is stored in every table an then use partitioned view in your function.
Partitioned view is a view composed of union all of some tables with the same structure, each of which has check constraint on the same column that defines the values this column is restricted to. When you use this view with the filter on country column, all the tables except for the correct one will be eliminated from execution plan thanks to check constraint defined on this column.
So you can change your function to accept the only parameter that is country and it will read only one table corresponding to parameter passed.
More on partitioned views here: Using Partitioned Views

Dynamic table name vs array column in static table

I'm using PostgreSQL to coordinate a large scale simulation which involves initializing various components of my application via arrays of integers. In particular, I have the notion of a "Controller", where each controller requires a variable number of parameters to be initialized.
I have a job table which stores the controller_id and a controller_parameters foreign key for actually linking to the set of parameters we want. My idea to start with was to do the following:
Use the controller_id to dynamically choose a table name from which to select the initialization parameters. Each of these tables would have a controller_parameters column that links the actual initialization data to the source table.
Once we know the table, run a SELECT * FROM #someController_parameters_table p WHERE p.controller_parameters = controller_parameters LIMIT 1;
Put these into a custom type which has an integer array field to be returned to the client.
The main problem is that this has Dynamic SQL, which I hear is not a good thing to do.
My proposed change is to have a new table, let's say controller_parameters which has the columns (controller_id, parameters_id, parameters[]). The third column stores the initialization parameters for an individual controller and parameter set.
Pros of this design are that we move back into static SQL land, which is good. Cons are that, when I generate the actual parameters to insert to the individual parameters table, I usually use a cross join to get all of the permutations of the individual parameters, and insert them accordingly to individual tables. I personally don't know how to take a cross-joined table row and convert it to an int[], so that's a potential roadblock.
Thoughts?
You can use array_agg to take the result of a group and turn it into an array.
select controller_id, parameters_id, array_agg(parameter)
from ...
group by controller_id, parameters_id
Here's postgresql docs on aggregate functions:
https://www.postgresql.org/docs/9.5/static/functions-aggregate.html

SQL Server : associate function to a column of a view

I have a function that sending a code gets a description through various formatings, I need to use the function inside a view in four columns but rather than calling it in the query I would like to associate it as a computed column (so we don't have to modify the query/view if the function name or parameters changes so the DBA can manage it in the future...)
Is it possible?
Yes - you can do something like:
ALTER TABLE dbo.YourTableNameHere
ADD ComputedColumn1 AS dbo.FunctionCall1(args......)
This adds a computed column to your table - and if the function is "deterministic", you can even add the PERSISTED keyword to it, so that the resulting value will actually be stored in the table (and the function isn't call every time you select a row).

SQL Server 2008: Custom Aggregate Function and Indexed Views

I'm having issues creating an index on a view that uses a custom CLR aggregate function.
I don't see any way to flag the aggregate function as deterministic or with schemabinding.
I'm creating my function like so:
CREATE ASSEMBLY StringUtil
AUTHORIZATION dbo
FROM 'C:\StringUtil.dll'
WITH PERMISSION_SET = UNSAFE
GO
CREATE AGGREGATE SUMSTRING (#input nvarchar(200))
RETURNS nvarchar(max) WITH SCHEMABINDING
EXTERNAL NAME StringUtil.Concatenate
And my view is defined as:
CREATE VIEW RolledValues WITH SCHEMABINDING
AS
SELECT ID, SumString(ValueText) as Value FROM [dbo].[IDValue]
GROUP BY ID
The issue occurs when I try to create an index on that view:
CREATE UNIQUE CLUSTERED INDEX IDX_RollValues_ID_VALUE on RolledValues (ID)
Error: Cannot create index on view "dbo.RolledValues" because it uses aggregate
"dbo.SumString". Consider eliminating the aggregate, not indexing the view, or
using alternate aggregates.
So is it possible to use a custom aggregate function in an indexed view? I cannot find any documentation on this...
The page on Creating Indexed Views lists a number of restrictions:
The SELECT statement in the view cannot contain the following Transact-SQL syntax elements:
...
A CLR user-defined aggregate function.
There's not even provision, at the current time, to describe a CLR aggregate as Deterministic (at least, if the API was going to be consistent). The SqlFunctionAttribute has an IsDeterministic property. No such property exists in SqlUserDefinedAggregateAttribute
It does help to reason about things if you consider why so many restrictions exist on indexed views.
The ones on aggregates have a pretty simple explanation - you're only allowed to use aggregates (such as SUM and COUNT_BIG) that have the property that SQL Server will be able to adjust the values, or add or remove rows from the index, based purely on the subset of rows that are the subject of the current transaction.
E.g. if the view has a row with ID=19, COUNT_BIG=5, and SUM=96, and a transaction deletes 3 rows with ID 19, whose SUM adds to 43, then it can update that row of the view to be COUNT_BIG=2 and SUM=53. Alternatively, if the transaction had deleted 5 rows with ID=19, it would have caused the row to be removed.
Note that in either case, we don't have to examine any other rows of the table to determine if they have ID=19.
So how could SQL Server hope to achieve similar functionality with a user defined aggregate? The current interface for user defined aggregates doesn't have the sort of support you'd need (it would need to have a trigger like interface also).

Resources