{"id":1332,"date":"2012-05-10T00:00:00","date_gmt":"2012-05-10T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/sql-view-basics\/"},"modified":"2021-08-16T15:02:01","modified_gmt":"2021-08-16T15:02:01","slug":"sql-view-basics","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/sql-view-basics\/","title":{"rendered":"SQL VIEW Basics"},"content":{"rendered":"<div id=\"pretty\">\n<p class=\"start\">A <code>VIEW<\/code> is a virtual table, defined by a query, that does not exist until it is invoked by name in an SQL statement. This may sound simple enough, but\u00a0 some developers have difficulties with the\u00a0 concept.\u00a0 Because of this, they tend to leave out <code>VIEW<\/code>s because they do not appreciate their value. It is easy to write a query and not bother to put it into a <code>VIEW<\/code>, because there is no performance boost. &#8216;If I am going to save code,&#8217; they reason, &#8216;I want it in a stored procedure that can take a parameter list instead.&#8217;<\/p>\n<p>In fact, a <code>VIEW<\/code> definition can be copied as in-line text, just like a CTE. But with a good optimizer, the SQL engine can decide that enough sessions are using the same <code>VIEW<\/code> and materialize it as a shared table. The CTE, in contrast, is strictly local to the statement in which it is declared.<\/p>\n<p><code>VIEW<\/code>s are often named incorrectly. A <code>VIEW<\/code> is a table, so it is named just like any other table. The name tells us what set of things it represents in the data model. The most common offender is the &#8220;Volkswagen&#8221; coder who prefixes or suffixes the <code>VIEW<\/code> name with &#8220;vw_&#8221; or &#8220;<code>VIEW<\/code>_&#8221; in violation of ISO-11179 rules. We do not mix data and meta data in a name. This is as silly as prefixing every noun in a novel with &#8220;n_&#8221; so the reader will know that\u00a0 the word is a noun in English grammar.<\/p>\n<p>The ANSI Standard SQL syntax for the <code>VIEW<\/code> definition is<\/p>\n<pre>CREATE VIEW &lt;table name&gt; [(&lt;VIEW column list&gt;)]\r\nAS &lt;query expression&gt; \r\n[WITH [&lt;levels clause&gt;] CHECK OPTION]\r\n&lt;levels clause&gt; ::= CASCADED | LOCAL\r\n<\/pre>\n<p>the <code>WITH CHECK OPTION<\/code> is a little known and less used feature that has been around since the SQL-86 Standards. The <code>&lt;levels clause&gt;<\/code> option in the <code>WITH CHECK OPTION<\/code> did not exist in Standards before SQL-92. It is not implemented in T-SQL dialect, so I will skip it. If you see it, just remember T-SQL defaults to <code>CASCADED<\/code> behavior.<\/p>\n<p>A <code>VIEW<\/code> has no physical existence in the database until it is invoked. You cannot put constraints on a <code>VIEW<\/code> for that reason. The name of the <code>VIEW<\/code> must be unique within the entire database schema, like a base table name. The <code>VIEW<\/code> definition cannot reference itself, since it does not exist yet. Nor can the definition reference only other <code>VIEW<\/code>s; the nesting of <code>VIEW<\/code>s must eventually resolve to underlying base tables. This only makes sense; if no base tables were involved, what would you be <code>VIEW<\/code>ing?<\/p>\n<p>You can either build a column name list in the <code>VIEW<\/code> header or inherit the column names from the <code>SELECT<\/code> statement. Building this list is usually just one quick &#8220;cut &amp; paste&#8221; and well worth it. This is why we do not ever use &#8220;<code>SELECT<\/code> *&#8221; in a <code>VIEW<\/code> definition in production code. When the columns of a base tables change, the definition of the &#8220;star&#8221; will also change. If you are lucky, you will get an error when the <code>VIEW<\/code> has too many or too few columns when it is invoked. If you are not so lucky, the <code>VIEW<\/code> will run and give you unexpected results. If you are unlucky, the <code>VIEW<\/code> will run and give you wrong answers that you use.<\/p>\n<p>Every few months, someone will post to a SQL forum asking how to use a parameter in a <code>VIEW<\/code>. They would never ask how to use a parameter in a base table. The sight of a <code>SELECT<\/code> statement instead of a list of column declarations throws their mindset in the wrong direction.<\/p>\n<h2>Mullins Heuristics for VIEWS<\/h2>\n<p>Programmers have rules and standards for creating base tables. The data element names should follow the ISO-11179 rules. We have to have a key. We can have all kinds of constraints. We can have Declarative Referential Integrity actions among other base tables. But how do you design a <code>VIEW<\/code>?<\/p>\n<p>Craig Mullins, a DB2 expert and author, gave the following rule to ensure that <code>VIEW<\/code>s are created in a responsible and useful manner. Simply stated, the <code>VIEW<\/code> creation strategy should be goal-oriented. <code>VIEW<\/code>s should be created only when they achieve a specific, reasonable goal. Each <code>VIEW<\/code> should have a specific application or business requirement that it fulfills before it is created. That requirement should be documented somewhere, preferably in a data dictionary.<\/p>\n<p>Although this rule seems obvious, <code>VIEW<\/code>s are implemented at some shops without much thought as to how they will be used. This can cause the number of <code>VIEW<\/code>s that must be supported and maintained to increase until so many <code>VIEW<\/code>s exist that it is impossible to categorize their uses. Nobody wants to take a chance and drop a <code>VIEW<\/code>, so they just write a new one. Whenever a base table used by a <code>VIEW<\/code> definition is changed, then all those <code>VIEW<\/code>s have to be re-compiled and checked. Since <code>VIEW<\/code>s can be built on top of <code>VIEW<\/code>s, this can be tricky.<\/p>\n<p>Unlike other virtual tables, a <code>VIEW<\/code> is defined in the schema information tables and its definition (not its content!) is persisted. This implies some privileges are needed to use, create, alter and drop <code>VIEW<\/code>s. The first question is do you need to have privileges on the base tables that build a <code>VIEW<\/code>? Yes, but not full privileges. The minimal privileges would be to use the base tables, so you can build the <code>VIEW<\/code>. But that does not mean that the user needs to be able to directly query or modify the base tables.<\/p>\n<p>The ideal design should give each user a set of <code>VIEW<\/code>s that make it look as if the schema was designed for just his or her use, without regard to the rest of the enterprise.<\/p>\n<p>This is most often done for security and privacy. The payroll clerk can see the salaries of other personnel and change them. But he cannot give himself a pay raise and try to get out of town before the police find out. He can see the minimum, maximum and average salary in each department, but not who is making which salary.<\/p>\n<p>The Data Control Language (DCL) is the third sub-language in SQL after DDL and DML. This is where the DBA can <code>GRANT<\/code>, <code>REVOKE<\/code> or <code>DENY<\/code> all kinds of schema object privileges. We spend almost no time on it in training classes, and failure to do it right can destroy your enterprise. As a generalization, the DBA ought to start with a list of roles users can play in the enterprise and create a script for the privileges each role needs. A new user can then be assigned a role and you do not have to repeat the script over and over.<\/p>\n<p>Do not grant <code>VIEW<\/code> creation privileges to everyone. The &#8220;nearly the same&#8221; <code>VIEW<\/code>s are a special problem. One user might have read the spec &#8220;Employees must be over 21 years of age to serve alcohol&#8221; to mean strictly over 21 as of today or can they pour a drink on their 21-st birthday? If <code>VIEW<\/code> creation had been left to just one data modeler, only one of these <code>VIEW<\/code>s would exist and it would have the correct business rule.<\/p>\n<h2>Tricky Queries and Computations<\/h2>\n<p>Not all programers are equal, so you can make sure that the <code>VIEW<\/code>s preserve the best work in your shop. The other advantage is that if someone finds a better query for the current state of the database, you keep the <code>VIEW<\/code> header, drop the <code>SELECT<\/code> statement in the body, replace it and then re-compile your code. The programmer needs no knowledge of how the <code>VIEW<\/code> works. This technique becomes more useful as the SQL becomes more complex.<\/p>\n<p>In T-SQL, we used to write complicated code to get sequence numbers and pure dates without time. This code was often hidden in <code>VIEW<\/code>s. The numbering can now be done with ROW_NUMBER() and we have a DATE data type since SQL Server 2008. In many cases, procedures and functions that used loops and fancy string manipulations can be replaced with <code>VIEW<\/code>s.<\/p>\n<h2>Updatable and Read-Only <code>VIEW<\/code>s<\/h2>\n<p>Unlike base tables, <code>VIEW<\/code>s are either updatable or read-only, but not both. <code>INSERT<\/code>, <code>UPDATE<\/code>, and <code>DELETE<\/code> operations are allowed on updatable <code>VIEW<\/code>s and base tables, subject to other constraints. <code>INSERT<\/code>, <code>UPDATE<\/code>, and <code>DELETE<\/code> are not allowed on read-only <code>VIEW<\/code>s, but you can change their base tables, as you would expect.<\/p>\n<p>An updatable <code>VIEW<\/code> is one that can have each of its rows associated with exactly one row in an underlying base table. When the <code>VIEW<\/code> is changed, the changes pass through the <code>VIEW<\/code> to that underlying base table unambiguously. Updatable <code>VIEW<\/code>s in Standard SQL are defined only for queries that meet these criteria<\/p>\n<ol>\n<li>They are built on only one table<\/li>\n<li>No <code>GROUP BY<\/code> clause<\/li>\n<li>No <code>HAVING<\/code> clause<\/li>\n<li>No aggregate functions<\/li>\n<li>No calculated columns<\/li>\n<li>No <code>UNION<\/code>, <code>INTERSECT<\/code> or <code>EXCEPT<\/code><\/li>\n<li>No <code>SELECT DISTINCT<\/code> clause<\/li>\n<li>Any columns excluded from the <code>VIEW<\/code> must be <code>NULL<\/code>-able or have a <code>DEFAULT<\/code> clause in the base table, so that a whole row can be constructed for insertion.<\/li>\n<\/ol>\n<p>By implication, the <code>VIEW<\/code> must also contain a key of the table. In short, we are absolutely sure that each row in the <code>VIEW<\/code> maps back to one and only one row in the base table. The major advantage of this limited definition is that it is based on syntax and not semantics. For example, these <code>VIEW<\/code>s are logically identical:<\/p>\n<pre>CREATE VIEW Foo1 (a, b, ..) -- updatable, has a key!\r\nAS SELECT (a, b, ..)\r\n\u00a0\u00a0\u00a0FROM Foobar\r\n\u00a0\u00a0WHERE x IN (1,2);\r\n<\/pre>\n<p>&nbsp;<\/p>\n<pre>CREATE VIEW Foo2 (a, b, ..)-- not updateable!\r\nAS SELECT (a, b, ..)\r\n\u00a0\u00a0\u00a0FROM Foobar\r\n\u00a0\u00a0WHERE x = 1\r\n\u00a0\u00a0UNION ALL\r\n\u00a0\u00a0SELECT (a, b, ..)\r\n\u00a0\u00a0\u00a0FROM Foobar\r\n\u00a0\u00a0WHERE x = 2;\r\n<\/pre>\n<p>But Foo1 is updateable and Foo2 is not. While I know of no formal proof, I suspect that determining if a complex query resolves to an updatable query for allowed sets of data values possible in the table is an NP-complete problem.<\/p>\n<p>The <code>INSTEAD OF<\/code> trigger was the ANSI Standards Committee letting the Data Modeler decide on how to resolve the <code>VIEW<\/code> updating problem. These triggers are added to a <code>VIEW<\/code> and are executed on base tables that make up the <code>VIEW<\/code>. The user never sees them fire and work their magic.<\/p>\n<p>\u00a0As an example, consider a <code>VIEW<\/code> that builds the total compensation for each employee by joining the personnel, employee stock holdings, bonuses and <code>salary_amt<\/code> tables in one <code>VIEW<\/code>. An <code>INSTEAD OF<\/code> trigger can update the total compensation using a hidden formula and complex business rules that the user never sees.<\/p>\n<p>The use of <code>INSTEAD OF<\/code> triggers gives the user the effect of a single table, but there can still be surprises. Think about three tables; A, B and C. Table C is disjoint from the other two. Tables A and B overlap. So I can always insert into C and may or may not be able to insert into A and B if I hit overlapping rows.<\/p>\n<p>Going back to my Y2K consulting days, I ran into a version of such a partition by calendar periods. Their Table C was set up on Fiscal quarters and got leap year wrong because one of the fiscal quarters ended on the last day of February.<\/p>\n<h2>Nested VIEWs<\/h2>\n<p>A point that is often missed, even by experienced SQL programmers, is that a <code>VIEW<\/code> can be built on other <code>VIEW<\/code>s. The only restrictions are that circular references within the query expressions of the <code>VIEW<\/code>s are illegal and that a <code>VIEW<\/code> must ultimately be built on base tables. One problem with nested <code>VIEW<\/code>s is that different updatable <code>VIEW<\/code>s can reference the same base table at the same time. If these <code>VIEW<\/code>s then appear in another <code>VIEW<\/code>, it becomes hard to determine what has happened when the highest-level <code>VIEW<\/code> is changed. As an example, consider a table with two keys:<\/p>\n<pre>CREATE TABLE CanadianDictionary\r\n(english_id INTEGER UNIQUE,\r\n\u00a0french_id INTEGER UNIQUE,\r\n\u00a0eng_word CHAR(30),\r\n\u00a0french_word CHAR(30),\r\n\u00a0CHECK (COALESCE (english_id, french_id) IS NOT NULL);\r\n<\/pre>\n<p>The table declaration is a bit strange. It allows an English-only or French-only word to appear in the table. But the <code>CHECK()<\/code> constraint requires that a word must fall into one or both type codes.<\/p>\n<pre>INSERT INTO CanadianDictionary\r\nVALUES (1, 2, 'muffins', 'croissants'),\r\n\u00a0\u00a0\u00a0\u00a0(2, 1, 'fish bait', 'escargots');\r\nCREATE VIEW EnglishWords\r\nAS SELECT english_id, eng_word\r\n\u00a0\u00a0\u00a0FROM CanadianDictionary\r\n\u00a0\u00a0WHERE eng_word IS NOT NULL;\r\nCREATE VIEW FrenchWords\r\nAS SELECT french_id, french_word\r\n\u00a0\u00a0\u00a0FROM CanadianDictionary\r\n\u00a0\u00a0WHERE french_word IS NOT NULL);\r\n<\/pre>\n<p>We have now tried the escargots and decided that we wish to change our opinion of them:<\/p>\n<pre>UPDATE EnglishWords\r\n\u00a0\u00a0SET eng_word = 'appetizer'\r\n\u00a0WHERE english_id = 2;\r\n<\/pre>\n<p>Our French user has just tried Haggis and decided to insert a new row for his experience:<\/p>\n<pre>UPDATE FrenchWords\r\n\u00a0\u00a0SET french_word = 'tripoux'\r\n\u00a0WHERE french_id = 3;\r\n<\/pre>\n<p>The row that is created is <code>(NULL, 3, NULL, 'tripoux')<\/code>, since there is no way for the <code>VIEW<\/code> <code>FrenchWords<\/code> to get to the <code>VIEW<\/code> <code>EnglishWords<\/code> columns. Likewise, the English <code>VIEW<\/code> user can construct a row to insert his translation, <code>(3, NULL, 'Haggis', NULL),<\/code> but neither of them can consolidate the two rows into a meaningful piece of data.<\/p>\n<p>To delete a row is also to destroy data; the French-speaker who drops &#8216;croissants&#8217; from the table also drops &#8216;muffins&#8217; from <code>VIEW<\/code> <code>EnglishWords.<\/code><\/p>\n<h3>WITH CHECK OPTION Clause<\/h3>\n<p>If <code>WITH CHECK OPTION<\/code> is specified, the <code>VIEW<\/code>ed table has to be updatable. This is actually a fast way to check how your particular SQL implementation handles updatable <code>VIEW<\/code>s. Try to create a version of the <code>VIEW<\/code> in question using the <code>WITH CHECK OPTION<\/code> and see if your product will allow you to create it. The <code>WITH CHECK OPTION<\/code> was part of the SQL-89 Standard, but nobody seems to know about it! Consider this skeleton:<\/p>\n<pre>CREATE VIEW V1\r\nAS SELECT key_col, col1, col2\r\n\u00a0\u00a0\u00a0FROM Foobar\r\n\u00a0\u00a0WHERE col1 = 'A';\r\n<\/pre>\n<p>and now <code>UPDATE<\/code> it with<\/p>\n<pre>UPDATE V1 SET col1 = 'B';\r\n<\/pre>\n<p>The <code>UPDATE<\/code> will take place without any trouble, but the rows that were previously seen now disappear when we use V1 again. They no longer meet the <code>WHERE<\/code> clause condition! Likewise, an <code>INSERT INTO<\/code> statement with <code>VALUES (col1 = 'B')<\/code> would insert just fine, but its rows would never be seen again in this <code>VIEW<\/code>. This might be the desired behavior. For example, you can set up a <code>VIEW<\/code> of rows in a jobs table with a status code of &#8216;to be done&#8217;, work on them, and change a status code to &#8216;finished&#8217;, and the rows will disappear from your <code>VIEW<\/code>. The important point is that the <code>WHERE<\/code> clause condition was checked only at the time when the <code>VIEW<\/code> was invoked.<\/p>\n<p>The <code>WITH CHECK OPTION<\/code> makes the system check the <code>WHERE<\/code> clause condition upon <code>INSERT<\/code> and <code>UPDATE<\/code>. If the new or changed row fails the test, the change is rejected and the <code>VIEW<\/code> remains the same. The <code>WITH CHECK OPTION<\/code> clause does not work like a CHECK constraint.<\/p>\n<pre>CREATE TABLE Foobar (col_a INTEGER);\r\nCREATE VIEW TestView (col_a)\r\nAS\r\nSELECT col_a FROM Foobar WHERE col_a &gt; 0\r\nWITH CHECK OPTION;\r\nINSERT INTO TestView VALUES (NULL); -- This fails!\r\nCREATE TABLE Foobar_2 (col_a INTEGER CHECK (col_a &gt; 0));\r\nINSERT INTO Foobar_2(col_a)\r\nVALUES (NULL); -- This succeeds!\r\n<\/pre>\n<p>The <code>WITH CHECK OPTION<\/code> must be <code>TRUE<\/code> while the CHECK constraint can be either <code>TRUE<\/code> or <code>UNKNOWN<\/code>. This is an example of the differences in DDL and DML in SQL. Once more, you need to watch out for <code>NULL<\/code>s.<\/p>\n<p>T-SQL checks all the underlying levels that built the <code>VIEW<\/code>, as well as the <code>WHERE<\/code> clause condition in the <code>VIEW<\/code> itself. If anything causes a row to disappear from the <code>VIEW<\/code>, the <code>UPDATE<\/code> is rejected. Consider two <code>VIEW<\/code>s built on each other from the Personnel table:<\/p>\n<pre>CREATE VIEW Low_Paid_Personnel (emp_id, salary_amt)\r\nAS SELECT emp_id, salary_amt \r\n\u00a0\u00a0\u00a0FROM Personnel\r\n\u00a0\u00a0WHERE salary_amt &lt;= 250.00;\r\nCREATE VIEW Medium_Paid_Personnel (emp_id, salary_amt)\r\nAS SELECT emp_id, salary_amt \r\n\u00a0\u00a0\u00a0FROM Low_Paid_Personnel\r\n\u00a0\u00a0WHERE salary_amt &gt;= 100.00;\r\n<\/pre>\n<p>If neither <code>VIEW<\/code> has a <code>WITH CHECK OPTION<\/code>, the effect of updating <code>Medium_Paid_Personnel <\/code>by increasing every <code>salary_amt<\/code> by $1,000 will be passed without any check to <code>Low_Paid_Personnel <\/code>. <code>Low_Paid_Personnel <\/code>will pass the changes to the underlying Personnel table. The next time <code>Medium_Paid_Personnel <\/code>is used, <code>Low_Paid_Personnel <\/code>will be rebuilt in its own right and <code>Medium_Paid_Personnel <\/code>rebuilt from it, and all the employees will disappear from <code>Medium_Paid_Personnel <\/code>.<\/p>\n<p>If only <code>Medium_Paid_Personnel <\/code>has a <code>WITH CHECK OPTION<\/code> on it, the <code>UPDATE<\/code> will fail. <code>Medium_Paid_Personnel <\/code>has no problem with such a large <code>salary_amt<\/code>, but it would cause a row in <code>Low_Paid_Personnel <\/code>to disappear, so <code>Medium_Paid_Personnel <\/code>will reject it. However, if only <code>Medium_Paid_Personnel <\/code>has a <code>WITH LOCAL CHECK OPTION<\/code> on it, the <code>UPDATE<\/code> will succeed. <code>Medium_Paid_Personnel <\/code>has no problem with such a large <code>salary_amt<\/code>, so it passes the change along to <code>Low_Paid_Personnel <\/code>. <code>Low_Paid_Personnel <\/code>, in turn, passes the change to the Personnel table and the <code>UPDATE<\/code> occurs. If both <code>VIEW<\/code>s have a <code>WITH CHECK OPTION<\/code>, the effect is a set of conditions, all of which have to be met. The Personnel table can accept <code>UPDATE<\/code>s or <code>INSERT<\/code>s only where the <code>salary_amt<\/code> is between $100 and $250.<\/p>\n<h2>WITH CHECK OPTION as Constraints<\/h2>\n<p>Lothar Flatz, an instructor for Oracle Software Switzerland made the observation that while Oracle cannot put subqueries into <code>CHECK()<\/code>() constraints and triggers would not be possible because of the mutating table problem, you can use a <code>VIEW<\/code> that has a <code>WITH CHECK OPTION<\/code> to enforce subquery constraints.<\/p>\n<p>For example, consider a hotel registry that needs to have a rule that you cannot add a guest to a room that another is or will be occupying. Instead of writing the constraint directly, like this:<\/p>\n<pre>CREATE TABLE Hotel\r\n(room_nbr INTEGER NOT NULL,\r\n\u00a0arrival_date DATE NOT NULL,\r\n\u00a0departure_date DATE NOT NULL,\r\n\u00a0guest_name CHAR(30) NOT NULL,\r\n\u00a0CONSTRAINT valid_stay_dates\r\n\u00a0CHECK (H1.arrival_date &lt;= H1.departure_date),\r\n\u00a0CONSTRAINT no_overlaps\r\n\u00a0CHECK (NOT EXISTS\r\n\u00a0\u00a0\u00a0\u00a0(SELECT *\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0FROM Hotel AS H1, Hotel AS H2\r\n\u00a0\u00a0\u00a0\u00a0\u00a0WHERE H1.room_nbr = H2.room_nbr\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0AND H2.arrival_date &lt; H1.arrival_date \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0AND H1.arrival_date &lt; H2.departure_date)));\r\n<\/pre>\n<p>The <code>valid_stay_dates<\/code> constraint is fine, since it has no subquery, but will choke on the <code>no_overlaps<\/code> constraint. Leaving the <code>no_overlaps<\/code> constraint off the table, we can construct a <code>VIEW<\/code> on all the rows and columns of the Hotel base table and add a <code>WHERE<\/code> clause which will be enforced by the <code>WITH CHECK OPTION<\/code>.<\/p>\n<pre>CREATE VIEW Valid_Hotel (room_nbr, arrival_date, departure_date, guest_name)\r\nAS SELECT H1.room_nbr, H1.arrival_date, H1.departure_date, H1.guest_name\r\n\u00a0\u00a0\u00a0FROM Hotel AS H1\r\n\u00a0\u00a0WHERE NOT EXISTS \r\n\u00a0\u00a0\u00a0\u00a0\u00a0(SELECT *\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0FROM Hotel AS H2\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0WHERE H1.room_nbr = H2.room_nbr\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0AND H2.arrival_date &lt; H1.arrival_date \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0AND H1.arrival_date &lt; H2.departure_date)\r\n\u00a0\u00a0\u00a0AND H1.arrival_date &lt;= H1.departure_date \r\n\u00a0\u00a0WITH CHECK OPTION;\r\n<\/pre>\n<p>For example,<\/p>\n<pre>INSERT INTO Valid_Hotel\r\nVALUES (1, '2012-06-01', '2012-06-03', 'Ron Coe'); \r\nGO\r\nINSERT INTO Valid_Hotel\r\nVALUES (1, '2012-06-03', '2012-06-05', 'John Doe');\r\n<\/pre>\n<p>will give a <code>WITH CHECK OPTION<\/code> clause violation on the second <code>INSERT INTO<\/code> statement, as we wanted.<\/p>\n<h2>Dropping <code>VIEW<\/code>s<\/h2>\n<p><code>VIEW<\/code>s, like tables, can be dropped from the schema. The T-SQL syntax for the statement is:<\/p>\n<pre>DROP VIEW &lt;table name list&gt;; <\/pre>\n<p>The use of the &lt;table name list&gt; is dialect and it gives you a shorthand for repeating drop statements. The drop behavior depends on your vendor. The usual way of storing <code>VIEW<\/code>s was in a schema information table is to keep the <code>VIEW<\/code> name, the text of the <code>VIEW<\/code>, but dependencies. When you drop a <code>VIEW<\/code>, the engine usually removes the appropriate row from the schema information tables. You find out about dependencies when you try to use something that wants the dropped <code>VIEW<\/code>s. Dropping a base table could cause the same problem when the <code>VIEW<\/code> was accessed. But the primary key\/foreign key dependencies among base tables will prevent dropping some base tables.<\/p>\n<h2>Table Expression <code>VIEW<\/code>s<\/h2>\n<p>An old usage for <code>VIEW<\/code>s was to do the work of CTEs when there were no CTEs. The programmers created <code>VIEW<\/code>s, and then used them. Of course they wasted space, caused disk reads and were only used in one statement. It might be worth looking at old code for <code>VIEW<\/code>s that are not shared.<\/p>\n<p>Today the reverse is true. Programmers create the same CTE code over and over in different queries and give it local names for each appearance. Those local names are seldom the same and are often &#8220;place markers&#8221; like &#8220;X&#8221; or &#8220;CTE_1&#8221; and give no hint as to what the table expression means in the data model.<\/p>\n<p>It can be hard to factor out common table expressions across multiple queries. One query uses an infixed JOIN operators and another uses a &lt;span class=&#8221;mono&#8221;&gt;FROM&lt;\/span&gt; list, the predicates are equivalent but written slightly different and so forth.<\/p>\n<p>I recommend that you sit down and think of useful <code>VIEW<\/code>s, write them and then see if you can find places where they would make the code easier to read and maintain. As an example, our hotel application will probably need to find vacant rooms by calendar date, compute an occupancy ratio by calendar date and other basic facts.<\/p>\n<p>Another bad use is the one <code>VIEW<\/code> per Base Table myth that was poplar with DB2 programmers years ago. The reasoning behind this myth was the applaudable desire to insulate application programs from database changes. All programs were to be written against <code>VIEW<\/code>s instead of base tables. When a change is made to the base table, the programs would not need to be modified because they access a <code>VIEW<\/code>, not the base table.<\/p>\n<p>This does not work in the long run. All you do is accumulate weird orphaned <code>VIEW<\/code>s. Consider the simplest type of database change &#8211; adding a column to a table. If you do not add the column to the <code>VIEW<\/code>, no programs can access that column unless another <code>VIEW<\/code> is created that contains the new column. But if you create a new <code>VIEW<\/code> every time you add a new column it will not take long for your schema to be swamped with <code>VIEW<\/code>s. Even more troublesome is the question of which <code>VIEW<\/code> should be used by which program. Similar arguments can be made for any structural change to the tables.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>SQL Views are essential for the database developer. However, it is common to see them misused, or neglected.  Joe Celko tackles an introduction to the subject, but there is something about the topic that makes it likely that even the experienced developer will find  out something new from reading it.&hellip;<\/p>\n","protected":false},"author":96214,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143525],"tags":[4242,4168,4149,4150,5134],"coauthors":[6781],"class_list":["post-1332","post","type-post","status-publish","format-standard","hentry","category-learn","tag-basics","tag-database","tag-learn-sql-server","tag-sql","tag-sql-prompt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1332","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/96214"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1332"}],"version-history":[{"count":5,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1332\/revisions"}],"predecessor-version":[{"id":83520,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1332\/revisions\/83520"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1332"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1332"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1332"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1332"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}