<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>views &#8211; SQLpowered.com</title>
	<atom:link href="https://sqlpowered.com/tag/views/feed/" rel="self" type="application/rss+xml" />
	<link>https://sqlpowered.com</link>
	<description>SQL Server + BI</description>
	<lastBuildDate>Sat, 15 Aug 2020 07:31:22 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://sqlpowered.com/wp-content/uploads/2020/07/FavIcon-e1594067873682-99x100.png</url>
	<title>views &#8211; SQLpowered.com</title>
	<link>https://sqlpowered.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Partitioned pohledy</title>
		<link>https://sqlpowered.com/partitioned-views/</link>
					<comments>https://sqlpowered.com/partitioned-views/#respond</comments>
		
		<dc:creator><![CDATA[Jan Dvořák]]></dc:creator>
		<pubDate>Sun, 12 Jul 2015 14:54:40 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[views]]></category>
		<guid isPermaLink="false">https://sqlpowered.com/?p=355</guid>

					<description><![CDATA[Partitioned pohledy představují mocný nástroj a rychlou alternativu ke klasickému partitioningu tabulek. Umožňují nám rozdělit velkou tabulku do několika menších tabulek. Data jsou mezi jednotlivými tabulkami rozdělena podle rozsahu hodnot v jednom sloupci, který jsme zvolili jako klíč. Nad tímto sloupcem je v každé tabulce pomocí CHECK omezení určen rozsah hodnot,...]]></description>
										<content:encoded><![CDATA[<p>Partitioned pohledy představují mocný nástroj a rychlou alternativu ke klasickému partitioningu tabulek. Umožňují nám rozdělit velkou tabulku do několika menších tabulek. Data jsou mezi jednotlivými tabulkami rozdělena podle rozsahu hodnot v jednom sloupci, který jsme zvolili jako klíč. Nad tímto sloupcem je v každé tabulce pomocí CHECK omezení určen rozsah hodnot, které můžou být v dané tabulce uloženy. Všechny tabulky jsou potom spojeny pomocí UNION ALL operátoru v jedno view. Pokud z tohoto view čteme data pomocí SELECT příkazu a v podmínce uvedeme sloupec, podle jehož hodnot jsme data rozdělili do jednotlivých tabulek, optimalizátor dotazů s využitím CHECK omezení správně určí, že požadovaná data se můžou nacházet pouze v určitých tabulkách a všechny ostatní tabulky při prohledávání ignoruje.<br />
Na jednoduchém příkladě si ukážeme, jak je partitioned pohledy naimplementovat a jak vypadají exekuční plány pro různé situace.<span id="more-355"></span></p>
<p>Nejprve si připravíme tabulku se sloupcem ID a naplníme ji hodnotami 1 až 30000:</p>
<pre class="lang:tsql decode:true">CREATE TABLE [dbo].[IdsTable]
   ( [Id] INT PRIMARY KEY )
GO

;WITH cte AS
(
	SELECT 1 ID
	UNION ALL
	SELECT ID + 1
	FROM [cte]
	WHERE cte.ID &lt; 30000
)
INSERT [dbo].[IdsTable]
	SELECT *
	FROM cte
	OPTION(MAXRECURSION 0)</pre>
<p>Vytvoříme tři testovací tabulky objednávek pro roky 2008 až 2010. Sloupec Id definujeme jako primární klíč a pomocí CHECK omezení určíme pro každou tabulku, že může obsahovat pouze hodnoty v uvedeném rozsahu:</p>
<pre class="lang:tsql mark:3,10,17 decode:true ">CREATE TABLE [dbo].[Orders2008]
(	[Id] INT PRIMARY KEY
         CHECK ([Id] BETWEEN 1 AND 10000),
    [ProductId] INT,
    [Amount] INT )
GO

CREATE TABLE [dbo].[Orders2009]
(   [Id] INT PRIMARY KEY,
         CHECK ([Id] BETWEEN 10001 AND 20000),
    [ProductId] INT,
    [Amount] INT )
GO

CREATE TABLE [dbo].[Orders2010]
(   [Id] INT PRIMARY KEY,
         CHECK ([Id] BETWEEN 20001 AND 30000),
    [ProductId] INT,
    [Amount] INT )
GO</pre>
<p>Tabulky naplníme testovacími daty. V tomu využijeme pomocnou tabulku IdsTable vytvořenou výše. Pomocí WHERE podmínky z ní vložíme do každé ze tří tabulek objednávek pouze hodnoty příslušného rozsahu:</p>
<pre class="lang:tsql decode:true ">INSERT [dbo].[Orders2008]
    SELECT [Id], [Id], ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) 
    FROM [dbo].[IdsTable]
    WHERE [Id] BETWEEN 1 AND 10000
GO

INSERT [dbo].[Orders2009]
    SELECT [Id], [Id], ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) 
    FROM [dbo].[IdsTable]
    WHERE [Id] BETWEEN 10001 AND 20000
GO

INSERT [dbo].[Orders2010]
    SELECT [Id], [Id], ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) 
    FROM [dbo].[IdsTable]
    WHERE [Id] BETWEEN 20001 AND 30000
GO</pre>
<p>Nyní vytvoříme vlastní view, v němž všechny tři tabulky spojíme v jeden set pomocí UNION ALL operátoru.</p>
<pre class="lang:tsql decode:true">CREATE VIEW [dbo].[v_Orders]
WITH SCHEMABINDING
AS
    SELECT [Id], [ProductId], [Amount] FROM [dbo].[Orders2008]
    UNION ALL
    SELECT [Id], [ProductId], [Amount] FROM [dbo].[Orders2009]
    UNION ALL
    SELECT [Id], [ProductId], [Amount] FROM [dbo].[Orders2010]
GO</pre>
<p>Jak se chovají jednotlivé dotazy proti našemu pohledu si ukážeme na následujících příkladech.</p>
<p>Výběr všech dat z pohledu se chová podle očekávání: SQL Server provede scan všech tří tabulek a výsledek složí do jediného setu.</p>
<pre class="lang:tsql decode:true">SELECT * FROM [dbo].[v_Orders]</pre>
<p><img fetchpriority="high" decoding="async" class="alignnone wp-image-1306 size-full" src="https://sqlpowered.com/wp-content/uploads/2015/07/PartitionedViews1.png" alt="PartitionedViews1" width="539" height="257" /></p>
<p>&nbsp;</p>
<p>Mnohem zajímavější situace nastává, pokud použijeme filtr podle sloupce, nad kterým jsme výše definovali CHECK omezení (partitioning column):</p>
<pre class="lang:tsql decode:true">SELECT * FROM [dbo].[v_Orders] WHERE [Id] = 5000</pre>
<p><img decoding="async" class="alignnone size-full wp-image-1307" src="https://sqlpowered.com/wp-content/uploads/2015/07/PartitionedViews2.png" alt="PartitionedViews2" width="511" height="72" /></p>
<p>Z exekučního plánu vidíme, že optimalizátor hledá data pouze v tabulce objednávek za rok 2009, protože námi hledaná hodnota 5000 spadá do rozsahu omezení, které jsme pro hodnoty v této tabulce definovali (1 až 10000)). Ostatní tabulky jsou z hledání vyloučeny logickou eliminací, neboť pouhým pohledem do metadat optimalizátor dokáže zjistit, že nad nimi definovaná CHECK omezení vylučují, aby se v nich hledaná hodnota mohla vykytovat.</p>
<p>Obdobným způsobem se chová i hledání podle kombinace různých hodnot. Opět jsou zahrnuty pouze tabulky, kde se hledaná hodnota může vyskytovat a vyloučeny ty, kde se hodnota nikdy vyskytnout nemůže:</p>
<pre class="lang:tsql decode:true">SELECT * FROM [dbo].[v_Orders] WHERE [Id] IN (5000, 15000)</pre>
<p><img decoding="async" class="alignnone size-full wp-image-1308" src="https://sqlpowered.com/wp-content/uploads/2015/07/PartitionedViews3.png" alt="PartitionedViews3" width="506" height="160" /></p>
<p>View lze využít i pro UPDATE, ale exekuční plán nás v tomto případě může překvapit, jak ukazuje následující příklad:</p>
<pre class="lang:tsql decode:true">UPDATE [dbo].[v_Orders]
    SET [Amount] = 0
WHERE [Id] = 5000</pre>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1309" src="https://sqlpowered.com/wp-content/uploads/2015/07/PartitionedViews4.png" alt="PartitionedViews4" width="624" height="250" /></p>
<p>Vidíme, že v exekučním plánu jsou zahrnuty všechny tři tabulky (Clustered Index Update). Důvodem je, že optimalizátor se snaží vygenerovat takový exekuční plán, který bude možné umístit do cache a využít později. Z toho důvodu musí být plán dostatečně robustní, aby platil i pro libovolné jiné vstupní hodnoty. Výše uvedený plán je tedy univerzální v tom smyslu, že bude použitelný i tehdy, pokud podmínka WHERE Id = X bude nabývat takových hodnot, že bude vždy proveden update v jiné základní tabulce, do jejíhož rozsahu omezení vstupní hodnota spadá.</p>
<p>Následující příklad nám ukazuje využití stejného univerzálního plánu na jiném rozsahu hodnot:</p>
<pre class="lang:tsql decode:true">UPDATE [dbo].[v_Orders]
    SET [Amount] = 0
WHERE [Id] BETWEEN 21000 AND 29000</pre>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1310" src="https://sqlpowered.com/wp-content/uploads/2015/07/PartitionedViews5.png" alt="PartitionedViews5" width="634" height="250" /></p>
<p>Pokud však přidáme do naší podmínky ještě sloupec Amount, vidíme, že exekuční plán již opět obsahuje část, která využívá logickou eliminaci (Clustered Index Seek):</p>
<pre class="lang:tsql decode:true ">UPDATE [dbo].[v_Orders]
    SET [Amount] = 0
WHERE [Id] BETWEEN 5000 AND 6000 AND [Amount] = 50</pre>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1311" src="https://sqlpowered.com/wp-content/uploads/2015/07/PartitionedViews6.png" alt="PartitionedViews6" width="926" height="251" /></p>
<p>Ve všech výše uvedených příkladech obsahovala WHERE podmínka sloupec, který jsme použili pro rozdělení dat do jednotlivých tabulek podle hodnot (CHECK omezení). Pokud však tento sloupec z WHERE podmínky vynecháme, není optimalizátor schopen využít logickou eliminaci a určit tabulky, ve kterých se hledané hodnoty nemohou vyskytovat. Nemá pak jinou možnost, než prohledat všechny tabulky a ztrácíme tím výhodu eliminace, přesně jak vidíme níže:</p>
<pre class="lang:tsql decode:true ">SELECT * FROM [dbo].[v_Orders] WHERE [Amount] = 0</pre>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1312" src="https://sqlpowered.com/wp-content/uploads/2015/07/PartitionedViews7.png" alt="PartitionedViews7" width="526" height="251" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlpowered.com/partitioned-views/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Vytvoření view s daty z uložené procedury</title>
		<link>https://sqlpowered.com/vytvoreni-view-s-daty-z-ulozene-procedury/</link>
					<comments>https://sqlpowered.com/vytvoreni-view-s-daty-z-ulozene-procedury/#respond</comments>
		
		<dc:creator><![CDATA[Jan Dvořák]]></dc:creator>
		<pubDate>Wed, 13 May 2015 06:02:43 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[views]]></category>
		<guid isPermaLink="false">https://sqlpowered.com/?p=654</guid>

					<description><![CDATA[Můžeme vytvořit view, ve kterém přímo zavoláme uloženou proceduru a vrátíme z ní data jako sloupce view? Ano, je to možné s pomocí funkce OPENROWSET, jak ukazuje příklad níže. Nicméně, opravdu věřím, že se s podobnou programátorskou kreativitou nesetkám v produkčním kódu:) CREATE VIEW v_test AS SELECT * FROM OPENROWSET...]]></description>
										<content:encoded><![CDATA[<p>Můžeme vytvořit view, ve kterém přímo zavoláme uloženou proceduru a vrátíme z ní data jako sloupce view? Ano, je to možné s pomocí funkce OPENROWSET, jak ukazuje příklad níže. Nicméně, opravdu věřím, že se s podobnou programátorskou kreativitou nesetkám v produkčním kódu:)</p>
<p><span id="more-654"></span></p>
<pre class="lang:tsql decode:true ">CREATE VIEW v_test AS
     SELECT *
     FROM OPENROWSET ('SQLOLEDB',  'SERVER=MyServer; Trusted_Connection=YES', 
                                    'SET FMTONLY OFF; EXEC sp_who')</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlpowered.com/vytvoreni-view-s-daty-z-ulozene-procedury/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to prevent views with outdated metadata?</title>
		<link>https://sqlpowered.com/how-to-prevent-views-with-outdated-metadata/</link>
					<comments>https://sqlpowered.com/how-to-prevent-views-with-outdated-metadata/#respond</comments>
		
		<dc:creator><![CDATA[Jan Dvořák]]></dc:creator>
		<pubDate>Fri, 23 Aug 2013 20:55:53 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[views]]></category>
		<guid isPermaLink="false">https://sqlpowered.com/?p=245</guid>

					<description><![CDATA[SQL Server is persisting various metadata when a new view is created in SQL Server. When underlying objects are changed later then view metadata aren&#8217;t updated automatically and this outdated metadata can cause several issues later. Let&#8217;s see one simple example. We will create a dbo.SampleTable table and create view...]]></description>
										<content:encoded><![CDATA[<p>SQL Server is persisting various metadata when a new view is created in SQL Server. When underlying objects are changed later then view metadata aren&#8217;t updated automatically and this outdated metadata can cause several issues later. Let&#8217;s see one simple example. We will create <em>a dbo.SampleTable</em> table and create view <em>v_Test</em> referencing this table. Then we will create two other views on top of each one of them.</p>
<pre class="lang:tsql decode:true">-- Create sample table
CREATE TABLE dbo.SampleTable
(
    Id VARCHAR(10)
)
GO

-- Create views for testing
CREATE VIEW dbo.v_Test
AS
    SELECT Id FROM dbo.SampleTable
GO

CREATE VIEW dbo.v_Test2
AS
    SELECT Id FROM dbo.v_Test
GO

CREATE VIEW dbo.v_Test3
AS
    SELECT Id FROM dbo.v_Test2
GO
</pre>
<p>Let&#8217;s check metadata persisted at views creation:</p>
<pre class="lang:tsql decode:true">SELECT 
    [o].[name], [t].[name] [TypeName], [c].[precision], [c].[scale]
FROM [sys].[columns] [c]
    INNER JOIN [sys].[objects] [o] ON [o].[object_id] = [c].[object_id]
	INNER JOIN [sys]. [t] ON [t].[system_type_id] = [c].[system_type_id] AND [t].[system_type_id] = [c].[system_type_id]
WHERE [o].[name] IN ('SampleTable', 'v_Test', 'v_Test2', 'v_Test3')
ORDER BY [o].[name]
GO</pre>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-4554" src="https://sqlpowered.com/wp-content/uploads/2013/08/Prevent-views-with-outdated-metadata-1.png" alt="" width="312" height="89" /></p>
<p>All views have it&#8217;s own metadata records in system tables saying that the underlying object column has VARCHAR data type. Le&#8217;ts go and change the data type in <em>dbo.SampleTable</em> from VARCHAR to INT and query metadata again.</p>
<pre class="lang:tsql decode:true">ALTER TABLE dbo.SampleTable
    ALTER COLUMN Id INT
GO
</pre>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-4555" src="https://sqlpowered.com/wp-content/uploads/2013/08/Prevent-views-with-outdated-metadata-2.png" alt="" width="312" height="89" /></p>
<p>Metadata are inconsistent now: Our base table has a column of INT datatype but all views on top of it are still holding the old information that it was VARCHAR previously. What to do now? Sure we can drop and recreate all views. But there is a better option: Let&#8217;s call the <a href="https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-refreshview-transact-sql?view=sql-server-ver15" rel="noopener noreferrer">sp_refreshview</a> system stored procedure and refresh the metadata of the <em>v_Test</em> view.</p>
<pre class="lang:tsql decode:true">EXEC sp_refreshview 'v_Test'
GO
</pre>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-4556" src="https://sqlpowered.com/wp-content/uploads/2013/08/Prevent-views-with-outdated-metadata-3.png" alt="" width="312" height="89" /></p>
<p>v_Test view has correct metadata now. But it&#8217;s obvious that we must do the same action for all remaining views.</p>
<pre class="lang:tsql decode:true">EXEC sp_refreshview 'v_Test2'
GO

EXEC sp_refreshview 'v_Test3'
GO
</pre>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-4557" src="https://sqlpowered.com/wp-content/uploads/2013/08/Prevent-views-with-outdated-metadata-4.png" alt="" width="312" height="89" /></p>
<p>Our database schema is consistent now. Congratulation. But isn&#8217;t it too much unpredictability and overhead to stay consistent? I can&#8217;t imagine application developers to take care of it when they are building tables. Most of the time they know zero about database metadata. So what are your options as DBA to prevent this to happen?</p>
<p>I see forced usage of the WITH SCHEMABINDING clause as the most reliable option. See more on how it works in this <a href="https://www.mssqltips.com/sqlservertip/4673/benefits-of-schemabinding-in-sql-server/" rel="noopener noreferrer">article</a>. Yes, developers will complain all the time they have more work to do and when there is a huge structure of view maintenance of scripts can become complicated. But all that assures that zero errors related to outdated views metadata will occur by your customer. I will call this as a best practice and you can enforce it in your CI/CD pipeline.</p>
<p>Another option is that you will simply script refresh view on every view changed or run some generic script to refresh all views after each release. There is such a sample one:</p>
<pre class="lang:tsql decode:true">DECLARE @Cnt INT = 0
DECLARE @Runs INT = 5 -- views will be updated up to this nested level
DECLARE @Stmt NVARCHAR(MAX) = ''

WHILE @Cnt &lt; @Runs
BEGIN

	SELECT @Stmt = @Stmt + 'EXEC sp_refreshview ''' + s.[name] + '.' + v.[name] + ''';' + CHAR(13)
	FROM sys.[views] v
		INNER JOIN sys.[schemas] s ON [s].[schema_id] = [v].[schema_id]

	PRINT @Stmt

	EXEC (@Stmt)

	SET @Cnt = @Cnt + 1

END</pre>
<p>It will load all views from the database and script EXEC sp_refreshview for each of them. It will repeat this until @Runs level is reached. This will handle the situation when views are nested and you should adjust it to your maximum known level of nesting. This is a brute-force solution but it works with minimum effort. Sure we can script it more in a more advanced way i.e. using <a href="https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-sql-expression-dependencies-transact-sql?view=sql-server-ver15" rel="noopener noreferrer">sys.sql_expression_dependencies</a> system view. I will try to write such a script for you later.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlpowered.com/how-to-prevent-views-with-outdated-metadata/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
