DML statement in a SQL Views - sql-server

I am trying to create a view in that view I want to insert a record or update a record based on perticular condition so can we insert or update in SQL view. Can we have insert or update statement in view?

Short answer: Yes. But there are restrictions.
Eg: (taken from http://msdn.microsoft.com/en-us/library/ms180800(v=sql.90).aspx )
Any modifications, including UPDATE, INSERT, and DELETE statements, must reference columns from only one base table.
The columns that are being modified in the view must reference the underlying data in the table columns directly. They cannot be derived in any other way, such as through:
An aggregate function (AVG, COUNT, SUM, MIN, MAX, GROUPING, STDEV, STDEVP, VAR and VARP). 2. A computation; the column cannot be computed from an expression using other columns. Columns formed using set operators (UNION, UNION ALL, CROSSJOIN, EXCEPT, and INTERSECT) amount to a computation and are also not updatable.
The columns that are being modified cannot be affected by GROUP BY, HAVING, or DISTINCT clauses.
TOP cannot be used anywhere in the select_statement of the view when WITH CHECK OPTION is also specified.
Sooo... if it is a relatively straight forward insert or update you're trying to achieve, then it is very possible - but the more complex the query (and the view), the more difficult it becomes.
I'd also offer some advice against doing this if you can - in my opinion it adds an unnecessary layer of complication and (as someone who has had to unravel others queries that use this process) it is a real pain to maintain.

No you cannot have an insert or update statement in a view. Refer to the documentation for CREATE VIEW
CREATE VIEW [ schema_name . ] view_name [ (column [ ,...n ] ) ]
[ WITH <view_attribute> [ ,...n ] ]
AS select_statement
[ WITH CHECK OPTION ] [ ; ]
It enforces that the definition must be a SELECT statement.

It would probably be better to instead use a stored procedure or function that returns a table.

Related

Rename underlying table without breaking dashboards in Metabase

We want to rename a table that is used in Metabase in the underlying database. Several dashboards depend on this table. Can we rename the underlying table and then ensure that the dashboards still work?
The quick way
By using a view in Snowflake, you should be able to rename the old table, while still allowing queries on the old name to still work.
https://docs.snowflake.com/en/sql-reference/sql/create-view.html
CREATE [ OR REPLACE ] [ SECURE ] [ RECURSIVE ] VIEW [ IF NOT EXISTS ] <name>
[ ( <column_list> ) ]
[ COPY GRANTS ]
[ COMMENT = '<string_literal>' ]
AS <select_statement>
This would look something like this:
CREATE VIEW old_name AS select * from new_name;
The long way
By querying the report_card table within the Metabase database, you can find the question numbers of all the questions that use the table, and then change the name in them.
SELECT * from report_card where dataset_query like '%old_table%'
You could automatically change the table name, but this would risk accidentally changing names that are similar or other breaking changes.

SQL: Detect whether an existing view supports INSERTs or UPDATEs

I have an application that allows users to define their own views in SQL and later have the application generate metadata about that view. This is mostly really easy as information_schema.columns tells me what the columns/types the view uses.
However, is there a way to find out whether the view can accept INSERT or UPDATE queries without doing something horrible like parsing the SQL myself or doing a test INSERT and checking for errors/rolling back the transaction?
I'm assuming SQL Server here, YMMV with other RDBMS products...
Assuming you're not using INSTEAD OF triggers, there is no flag or query that indicates whether a view is updatable or not, probably because a view can be updatable for certain classes of updates but not others.
Per the documentation:
Updatable Views
You can modify the data of an underlying base table
through a view, as long as the following conditions are true:
Any modifications, including UPDATE, INSERT, and DELETE statements, must reference columns from only one base table.
The columns being modified in the view must directly reference the underlying data in the table columns.
The columns cannot be derived in any other way, such as through the following: An aggregate function: AVG, COUNT, SUM, MIN, MAX,
GROUPING, STDEV, STDEVP, VAR, and VARP.
The column cannot be computed from an expression that uses other columns. Columns that are formed by using the set operators UNION,
UNION ALL, CROSS JOIN, EXCEPT, and INTERSECT amount to a
computation and are also not updatable.
The columns being modified are not affected by GROUP BY, HAVING, or DISTINCT clauses.
TOP is not used anywhere in the select_statement of the view together with the WITH CHECK OPTION clause.
The previous restrictions apply to any subqueries in the FROM
clause of the view, just as they apply to the view itself. Generally,
the Database Engine must be able to unambiguously trace modifications
from the view definition to one base table. For more information, see
Modify Data Through a View.
So you might have a view that is based off a JOIN, where certain updates (involving only one base table) were legal, while others (involving multiple base tables) were not.

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).

Merge data object table with associated attributes table in a view

Here's the setup: I have several tables that hold information for data objects which have the potential to have various and sundry bits of data associated with them. Each of these tables has an associated attributes table, which holds 3 bits of information:
the id (integer) of the row the attribute is associated with
a short attribute name ( < 50 chars )
a value (varchar)
The object table will have any number of columns of varying data types, but will always have an integer primary key. If possible, I would like to set up a view that will allow me to select a row from the object table, and all of its associated attributes at one go.
****EDIT****
Ideally, the form I'd like this to take is having columns in the view with the names of the matched attribute from the attributes table, and the value as the value of the attribute.
So for example, if I have table Foo with columns 'Bar', 'Bat', and 'Baz' the view would have those columns, and additionally, columns for any attributes that a row might have.
****END EDIT****
Now, I know (or think I do) that SQL doesn't allow using variables as an alias for a column name. Is there a clean, practical way of doing what I want, or am I chasing a pipe dream?
The obvious solution is to handle all of this in the application code, but I'm curious if it can be done in SQL.
The answer depends on what you are actually seeking. Will the output of the view have one row per attribute per object or one column per attribute per object? If the former, then I'm not sure why you need a view:
Select ...
From ObjectTable
Join AttributeTable
On AttributeTable.Id = ObjectTable.Id
However, I suspect what you want is the later or something like:
Select ...
, ... As Attribute1
, ... As Attribute2
, ... As Attribute3
...
From ObjectTable
In this scenario, the columns that would be generated are not known at execution because the attribute names are dynamic. This is commonly known as a dynamic crosstab. In general, the SQL language is not designed for dynamic column generation. The only way to do this in T-SQL is to use some fugly dynamic SQL. Thus, it is better done in a reporting tool or in middle-tier code.
It sounds like you want a view for each of your 'object' tables as well as its 'attributes' table. Correct me if I am wrong in my reading. It's not clear what your intentions are with 'using variables as an alias for a column name'. Were you hoping to merge ALL your objects, with their different columns, into one view?
Suggest create one view per entity table, and join to its relevant 'attributes' table.
Question though - why is there one matching attributes table for each entity table? Why are they split out? Perhaps you've made the question simpler or obfuscated, so perhaps my question is rhetorical.
CREATE VIEW Foo AS
SELECT O.ID
,O.EverythingElse
,A.ShortName
,A.SomeVarcharValue
FROM
ObjectTable AS O --customer, invoice, whathaveyou
INNER JOIN
ObjectAttribute AS A ON A.ObjectID = O.ID
To consume from this, you could:
SELECT * FROM Foo WHERE ID = 4 OR
SELECT * FROM Foo WHERE ShortName = 'Ender'

Updateable view in mssql with multiple tables and computed values

Huge database in mssql2005 with big codebase depending on the structure of this database.
I have about 10 similar tables they all contain either the file name or the full path to the file. The full path is always dependent on the item id so it doesn't make sense to store it in the database. Getting useful data out of these tables goes a little like this:
SELECT a.item_id
, a.filename
FROM (
SELECT id_item AS item_id
, path AS filename
FROM xMedia
UNION ALL
-- media_path has a different collation
SELECT item_id AS item_id
, (media_path COLLATE SQL_Latin1_General_CP1_CI_AS) AS filename
FROM yMedia
UNION ALL
-- fullPath contains more than just the filename
SELECT itemId AS item_id
, RIGHT(fullPath, CHARINDEX('/', REVERSE(fullPath))-1) AS filename
FROM zMedia
-- real database has over 10 of these tables
) a
I'd like to create a single view of all these tables so that new code using this data-disaster doesn't need to know about all the different media tables. I'd also like use this view for insert and update statements. Obviously old code would still rely on the tables to be up to date.
After reading the msdn page about creating views in mssql2005 I don't think a view with SCHEMABINDING would be enough.
How would I create such an updateable view?
Is this the right way to go?
Scroll down on the page you linked and you'll see a paragraph about updatable views. You can not update a view based on unions, amongst other limitations. The logic behind this is probably simple, how should Sql Server decide on what source table/view should receive the update/insert?
You can modify partitioned views, provided they satisfy certain conditions.
These conditions include having a partitioning column as a part of the primary key on each table, and having a set on non-overlapping check constraints for the partitioning column.
This seems to be not your case.
In your case, you may do either of the following:
Recreate you tables as views (with computed columns) for your legacy soft to work, and refer to the whole table from the new soft
Use INSTEAD OF triggers to update the tables.
If a view is based on multiple base tables, UPDATE statement on the view may or may not work depending on the UPDATE statement. If the UPDATE statement affects multiple base tables, SQL server throws an error. Whereas, if the UPDATE affects only one base table in the view then the UPDATE will work (Not correctly always). The insert and delete statements will always fail.
INSTEAD OF Triggers, are used to correctly UPDATE, INSERT and DELETE from a view that is based on multiple base tables. The following links has examples along with a video tutorial on the same.
INSTEAD OF INSERT Trigger
INSTEAD OF UPDATE Trigger
INSTEAD OF DELETE Trigger

Resources