<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vlad Ganușceac</title>
    <description>The latest articles on DEV Community by Vlad Ganușceac (@vladg_dev).</description>
    <link>https://dev.to/vladg_dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png</url>
      <title>DEV Community: Vlad Ganușceac</title>
      <link>https://dev.to/vladg_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vladg_dev"/>
    <language>en</language>
    <item>
      <title>Getting Started With Seal Report: Mapping Restrictions to SQL User-Defined Functions</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Sun, 05 Apr 2026 12:28:58 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-mapping-restrictions-to-sql-user-defined-functions-4p33</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-mapping-restrictions-to-sql-user-defined-functions-4p33</guid>
      <description>&lt;p&gt;This is the sixth post in the series.&lt;/p&gt;

&lt;p&gt;It is quite common that restrictions applied at the &lt;strong&gt;data model property level&lt;/strong&gt; (regardless of their origin—expression filters, static or dynamic enumerations, etc.) cannot implement the complex business logic that a &lt;strong&gt;stored procedure&lt;/strong&gt; or a &lt;strong&gt;user-defined function&lt;/strong&gt; can handle.&lt;/p&gt;

&lt;p&gt;Another motivation is related to &lt;strong&gt;custom joins&lt;/strong&gt; defined in the generated SQL statement. In such cases, it is not always optimal to rely on &lt;code&gt;JOIN&lt;/code&gt; statements generated automatically (under the hood) instead of using &lt;code&gt;EXISTS&lt;/code&gt; statements that a database administrator might prefer for performance or clarity reasons.&lt;/p&gt;

&lt;p&gt;The goal of this post is to demonstrate how to map &lt;strong&gt;data model restrictions&lt;/strong&gt; to the parameters of a &lt;strong&gt;user-defined function (UDF)&lt;/strong&gt; or &lt;strong&gt;stored procedure&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining a "Virtual" Table
&lt;/h2&gt;

&lt;p&gt;This exercise mostly &lt;strong&gt;mimics what was described in the previous post&lt;/strong&gt;:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p" class="crayons-story__hidden-navigation-link"&gt;Getting Started With Seal Report: Applying Custom Joins&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" alt="vladg_dev profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3437412" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 2&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p" id="article-link-3437412"&gt;
          Getting Started With Seal Report: Applying Custom Joins
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sql"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sql&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;The custom (non-base) table in this post will use the following &lt;strong&gt;user-defined function (UDF)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;udf_get_persons&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;state_province_ids&lt;/span&gt; &lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;department_ids&lt;/span&gt; &lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; 
        &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MiddleName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BirthDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaritalStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JobTitle&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;
    &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;HumanResources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;
        &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;state_province_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;'NULL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
            &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityAddress&lt;/span&gt; &lt;span class="n"&gt;entity_address&lt;/span&gt;
                &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;
                    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddressID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entity_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddressID&lt;/span&gt;
                &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;entity_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
                &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StateProvinceID&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;TRY_CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;STRING_SPLIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;state_province_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'('&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;')'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="s1"&gt;','&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;AND&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;department_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;'NULL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
            &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;HumanResources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmployeeDepartmentHistory&lt;/span&gt; &lt;span class="n"&gt;edh&lt;/span&gt;
                &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;edh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
                &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;edh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EndDate&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
                &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;edh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DepartmentID&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;TRY_CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;STRING_SPLIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;department_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'('&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s1"&gt;')'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="s1"&gt;','&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The user-defined function is already adapted to &lt;strong&gt;Seal Report conventions&lt;/strong&gt;: it accepts multiple selected values as parameters, or &lt;code&gt;NULL&lt;/code&gt; if nothing is selected.&lt;/p&gt;

&lt;p&gt;The SQL above demonstrates an example that &lt;strong&gt;Seal Report would not generate automatically&lt;/strong&gt;. The motivation is straightforward: if no properties from the joined table need to be selected, why use a &lt;code&gt;JOIN&lt;/code&gt; statement instead of an &lt;code&gt;EXISTS&lt;/code&gt; condition?&lt;/p&gt;

&lt;p&gt;The main difference is that the UDF shown above should be invoked like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;udf_get_persons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'{CommonRestriction_ProvinceIds}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'{CommonRestriction_DepartmentIds}'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you receive errors when using the &lt;strong&gt;Refresh columns (F9)&lt;/strong&gt; and &lt;strong&gt;Check SQL&lt;/strong&gt; options, don’t worry — here is the workaround:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Temporarily use default values as parameters in order to generate the required table columns:&lt;/p&gt;


&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;udf_get_persons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Applying the UDF to the Data Model
&lt;/h2&gt;

&lt;p&gt;The next step is to define &lt;strong&gt;Common restrictions and values&lt;/strong&gt; within the data model. However, these will not appear unless at least one property from the virtual table is added to the &lt;strong&gt;Elements&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;So, let’s drag and drop all the properties available in our virtual table into the &lt;strong&gt;Row Elements&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;Once the virtual table exists and its properties are mapped to the data model, it becomes possible to map the UDF input parameters to the model restrictions.&lt;/p&gt;

&lt;p&gt;Next, navigate to &lt;strong&gt;Models → Model → Model Definition → Common restrictions and values&lt;/strong&gt; to configure the restrictions.&lt;/p&gt;

&lt;p&gt;The two UDF restrictions should already be available for configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgkxmqji5kcpj53y7naw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgkxmqji5kcpj53y7naw.png" alt="UDF's constraints available for set up" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These common restrictions should be linked to &lt;strong&gt;dynamic select lists&lt;/strong&gt; by design.&lt;/p&gt;

&lt;p&gt;I previously covered how to configure dynamic select lists here:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Static and Dynamic Select Lists&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" alt="vladg_dev profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3404716" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 26&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" id="article-link-3404716"&gt;
          Getting Started with Seal Report: Static and Dynamic Select Lists
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;If you didn’t identify the required configuration for the common restrictions, here is my version:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmnrl32zvs4lp7mpd6bd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmnrl32zvs4lp7mpd6bd.png" alt="A working configuration" width="698" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final result should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy1e4s63llrtyrq2455k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcy1e4s63llrtyrq2455k.png" alt=" " width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When using an MSSQL stored procedure, what limitation prevents you from implementing the same approach as with a UDF?&lt;/p&gt;

&lt;p&gt;To better understand the behavior, try applying different filters. You may encounter issues when using &lt;strong&gt;multi-select values of type Text&lt;/strong&gt;. In such cases, consider using &lt;strong&gt;a single value per parameter&lt;/strong&gt; instead.&lt;/p&gt;

&lt;p&gt;This example demonstrates several edge-case scenarios that are very useful in real-life reporting situations&lt;/p&gt;

&lt;p&gt;In the next post, I will demonstrate how to create &lt;strong&gt;shared filters across multiple UI elements&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>analytics</category>
      <category>tutorial</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Getting Started With Seal Report: Applying Custom Joins</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Thu, 02 Apr 2026 17:22:45 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-applying-custom-joins-322p</guid>
      <description>&lt;p&gt;This is the fifth post in the series.&lt;/p&gt;

&lt;p&gt;Retrieving data automatically from multiple tables is powerful. However, in many real-world reporting scenarios, dashboards and analytical reports rely on &lt;strong&gt;custom SQL expressions&lt;/strong&gt;, &lt;strong&gt;user-defined functions&lt;/strong&gt;, or &lt;strong&gt;stored procedures&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this post, I’ll show how to link SQL expressions with other tables so you can benefit from &lt;strong&gt;both approaches at the same time&lt;/strong&gt; - keeping Seal Report’s automatic joins and filters while still using custom SQL logic.&lt;/p&gt;

&lt;p&gt;As a prerequisite, you should have an instance of the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; database available and be familiar with configuring a data source connection in &lt;strong&gt;Seal Report&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Previously, we've covered up how to apply &lt;strong&gt;dependent filters&lt;/strong&gt;:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Applying Dependant Filters&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" alt="vladg_dev profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3422199" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 29&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a" id="article-link-3422199"&gt;
          Getting Started with Seal Report: Applying Dependant Filters
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sql"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sql&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;In this post, we will rewrite the report created in the previous article using a different approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrieving data from the &lt;strong&gt;Person.Person&lt;/strong&gt; table together with data from the &lt;strong&gt;HumanResources&lt;/strong&gt; schema using a &lt;strong&gt;single SQL expression&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;applying a custom join between this SQL expression and the &lt;strong&gt;Person.Person&lt;/strong&gt; table;&lt;/li&gt;
&lt;li&gt;keeping the rest of the &lt;strong&gt;dynamic and custom filters&lt;/strong&gt; available, just like in the previous posts in this series.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The SQL statement used in this example will &lt;strong&gt;not&lt;/strong&gt; be implemented as a parameterized &lt;strong&gt;user-defined function&lt;/strong&gt; or &lt;strong&gt;stored procedure&lt;/strong&gt;, as that scenario will be covered in the next article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JobTitle&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BirthDate&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaritalStatus&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gender&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MiddleName&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pay_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PayFrequency&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pay_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rate&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;HumanResources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;
    &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;HumanResources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmployeePayHistory&lt;/span&gt; &lt;span class="n"&gt;pay_history&lt;/span&gt;
        &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pay_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
    &lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;
        &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Joining Data Tables Within the Data Source
&lt;/h2&gt;

&lt;p&gt;To add the SQL expression as a virtual table, open &lt;strong&gt;Sources → Data Source → Tables&lt;/strong&gt; and choose &lt;strong&gt;Add Table&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the configuration panel on the right side, set the &lt;strong&gt;Name&lt;/strong&gt; property to &lt;code&gt;Employees_Virtual&lt;/code&gt;, then paste the SQL query into &lt;strong&gt;SQL Select Statement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After that, press &lt;strong&gt;F9&lt;/strong&gt; (or &lt;strong&gt;Refresh columns&lt;/strong&gt;) so that Seal Report loads the column definitions from the query.&lt;/p&gt;

&lt;p&gt;At this point, the SQL expression becomes available as a regular table inside the &lt;strong&gt;data source&lt;/strong&gt; and can be used later when defining joins.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that this does &lt;strong&gt;not&lt;/strong&gt; create a new table in the database — the table exists only at the data source level in Seal Report.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following data tables should be loaded from the &lt;strong&gt;Catalog&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Person.Person&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person.BusinessEntity&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person.BusinessEntityAddress&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person.Address&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person.StateProvince&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Person.CountryRegion&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After loading them, the &lt;strong&gt;Tables&lt;/strong&gt; section should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia3sihpca8fu064ali9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia3sihpca8fu064ali9s.png" alt="Used Data Tables for This Example" width="264" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it is time to apply a custom join statement. Open &lt;strong&gt;Sources → Data Source → Joins&lt;/strong&gt; and use the &lt;strong&gt;Add Join&lt;/strong&gt; option from the context menu.&lt;/p&gt;

&lt;p&gt;Since the required join is &lt;strong&gt;bi-directional&lt;/strong&gt;, it does not matter whether the virtual table is defined as the left or the right side of the join. In the &lt;strong&gt;Join clause&lt;/strong&gt;, apply the following condition:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;Employee_Virtual&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BusinessEntityID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The joins used in this example are shown in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88sr224cbbeujn01ov0g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88sr224cbbeujn01ov0g.png" alt="Used Joins for This Example" width="398" height="217"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating Pivot Table and Using Custom Filters
&lt;/h2&gt;

&lt;p&gt;This time, the elements used for data visualization will be retrieved from a single source — the &lt;strong&gt;Employee_Virtual&lt;/strong&gt; custom table.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Row Elements&lt;/strong&gt; should contain the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FirstName&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MiddleName&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LastName&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JobTitle&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BirthDate&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MaritalStatus&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gender&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the pivoted part, the &lt;strong&gt;Column Elements&lt;/strong&gt; should contain &lt;strong&gt;PayFrequency&lt;/strong&gt;, and the &lt;strong&gt;Data Elements&lt;/strong&gt; should contain the &lt;strong&gt;Rate&lt;/strong&gt; property.&lt;/p&gt;

&lt;p&gt;Let’s apply the &lt;em&gt;what-I-see-is-what-I-can-filter&lt;/em&gt; principle to the static part of the table definition. Drag and drop the same properties used within the &lt;strong&gt;Row Elements&lt;/strong&gt; into the &lt;strong&gt;Restriction&lt;/strong&gt; section. Do not forget to enable &lt;strong&gt;prompt restrictions&lt;/strong&gt; for the newly added expressions in that section.&lt;/p&gt;

&lt;p&gt;If everything has been configured correctly, the model definition should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr49tnm9rlf1hj10363e0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr49tnm9rlf1hj10363e0.png" alt="Data model definitions" width="681" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you had any difficulties completing this exercise, you may take a look at the following post:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Creating a Pivot Table with Custom Filters&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" alt="vladg_dev profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3383374" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 22&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" id="article-link-3383374"&gt;
          Getting Started with Seal Report: Creating a Pivot Table with Custom Filters
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;Only one step remains to reach the final goal — applying a filter on a table other than the custom one and verifying that it influences the report output.&lt;/p&gt;

&lt;p&gt;Since we defined a custom join between the &lt;strong&gt;Employee_Virtual&lt;/strong&gt; table and the base table (&lt;strong&gt;Person.Person&lt;/strong&gt;), we now need to apply a restriction within the &lt;strong&gt;Person&lt;/strong&gt; schema to complete the scenario.&lt;/p&gt;

&lt;p&gt;Let’s drag and drop the &lt;strong&gt;CountryRegionCode&lt;/strong&gt; field from &lt;strong&gt;Person.CountryRegion&lt;/strong&gt; into the &lt;strong&gt;Restrictions&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;For example, applying the &lt;strong&gt;FR&lt;/strong&gt; region code will return just one row in the table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvcsbx6b4sbiwtwvj8yp3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvcsbx6b4sbiwtwvj8yp3.png" alt="Applying a filter reached from custom Join" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The example above would be even more useful if &lt;strong&gt;dependent filters&lt;/strong&gt; were applied to &lt;strong&gt;CountryRegion&lt;/strong&gt; and &lt;strong&gt;StateProvince&lt;/strong&gt;. Try implementing them yourself. If you encounter any difficulties, the previous post explains how to configure them.&lt;/p&gt;

&lt;p&gt;So far, so good. In the next post, I will show how to pass parameters into &lt;strong&gt;user-defined functions&lt;/strong&gt; and &lt;strong&gt;stored procedures&lt;/strong&gt; when using them as data sources in Seal Report.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>analytics</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with Seal Report: Applying Dependant Filters</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Sun, 29 Mar 2026 11:18:19 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-applying-dependant-filters-3j7a</guid>
      <description>&lt;p&gt;This is the fourth report in the series.&lt;/p&gt;

&lt;p&gt;So far, we’ve used different types of restrictions, but those were independent of each other. However, real-world filtering scenarios often require dependent restrictions, where one selection affects the available values of another.&lt;/p&gt;

&lt;p&gt;Within the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; open-source database, there is a classic example of such dependency: &lt;strong&gt;CountryRegion → StateProvince&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Applying this type of filter allows us to narrow down employees based on the province associated with one of their recorded addresses, while ensuring that only provinces belonging to the selected country are available for selection.&lt;/p&gt;

&lt;p&gt;In the previous post, we examined &lt;strong&gt;static&lt;/strong&gt; and &lt;strong&gt;dynamic select lists&lt;/strong&gt; as data table restrictions.&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Static and Dynamic Select Lists&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" alt="vladg_dev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3404716" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 26&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" id="article-link-3404716"&gt;
          Getting Started with Seal Report: Static and Dynamic Select Lists
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;In this article, we will configure &lt;strong&gt;dependent select lists&lt;/strong&gt; so that the available &lt;strong&gt;StateProvince&lt;/strong&gt; values automatically adjust based on the selected &lt;strong&gt;CountryRegion&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Necessary Data Tables from the Catalog
&lt;/h2&gt;

&lt;p&gt;To accomplish the desired task, several data tables must first be imported from the Catalog. The process was described in the first article of this series:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Building Your First Data Table from SQL Server&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" alt="vladg_dev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3381026" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 21&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" id="article-link-3381026"&gt;
          Getting Started with Seal Report: Building Your First Data Table from SQL Server
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;For the current scenario, the following data tables need to be imported from the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; database: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Person.CountryRegion&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person.StateProvince&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person.Address&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person.BusinessEntityAddress&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person.BusinessEntity&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tables allow us to build the relationship chain required to filter employees by the province associated with their addresses, while ensuring that the selected province belongs to the chosen country.&lt;/p&gt;

&lt;p&gt;If the &lt;strong&gt;Auto create joins&lt;/strong&gt; option was enabled during import, the records in the &lt;strong&gt;Joins&lt;/strong&gt; section should look similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mqvbt6eerwt7libk2av.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mqvbt6eerwt7libk2av.png" alt="Applied Auto Joins on Imported Data Tables from the Catalog"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the Main Enumeration
&lt;/h2&gt;

&lt;p&gt;The process is straightforward. Add a dynamic enumeration using the following &lt;strong&gt;SQL Select Statement&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;CountryRegionCode&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CountryRegion&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the values are retrieved from the query, the enumeration must be configured to load its content dynamically from the &lt;strong&gt;SQL Select Statement&lt;/strong&gt; and refreshed before report execution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I've named this enumeration as &lt;strong&gt;CountryRegion&lt;/strong&gt;. This enumeration name will be referenced later when configuring the dependent select list.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsauf4hhudvb8oq3y2afy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsauf4hhudvb8oq3y2afy.png" alt="Configuring the Dynamic Main Enumeration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, open &lt;strong&gt;Models → Model&lt;/strong&gt; and drag the &lt;strong&gt;CountryRegionCode&lt;/strong&gt; property from the &lt;strong&gt;Person.CountryRegion&lt;/strong&gt; data table into the &lt;strong&gt;Restrictions&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;The newly created restriction can now be linked to the &lt;strong&gt;CountryRegion&lt;/strong&gt; enumeration by selecting it as the custom enumerated list from the &lt;strong&gt;Advanced&lt;/strong&gt; configuration panel.&lt;/p&gt;

&lt;p&gt;Finally, enable &lt;strong&gt;Prompt at execution&lt;/strong&gt; so the restriction becomes available when running the report.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the Dependant Enumeration
&lt;/h2&gt;

&lt;p&gt;Things become slightly more interesting when configuring the dependent enumeration.&lt;/p&gt;

&lt;p&gt;At first glance, its main configuration looks similar to the previous example. The base &lt;strong&gt;SQL Select Statement&lt;/strong&gt; still loads all available values and does not reference any restrictions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;StateProvinceID&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StateProvince&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dependency itself is defined separately, inside &lt;strong&gt;Dynamic display → SQL Select Statement for prompted restriction&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;StateProvinceID&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StateProvince&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;CountryRegionCode&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;EnumValues_CountryRegion&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dependency is implemented inside the &lt;strong&gt;SQL Select Statement for prompted restriction&lt;/strong&gt;, where the selected values of the &lt;strong&gt;CountryRegion&lt;/strong&gt; restriction are referenced using the {EnumValues_&lt;em&gt;CountryRegion&lt;/em&gt;} pattern. This ensures that only the provinces belonging to the selected country remain available in the &lt;strong&gt;StateProvince&lt;/strong&gt; enumeration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7v0axq1uw1wlrm87n71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7v0axq1uw1wlrm87n71.png" alt="Configuration of Dependant Enumeration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The restriction itself is then added to the report in the same way as in the previous examples, so no additional configuration is required at the model level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;If everything has been configured as described in this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;initially, the &lt;strong&gt;StateProvince&lt;/strong&gt; filter remains empty because no country or region has yet been selected in the &lt;strong&gt;CountryRegion&lt;/strong&gt; filter:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2z9zml6xr9mgim16qs9y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2z9zml6xr9mgim16qs9y.png" alt="By Default, the Dependant Select List is Empty"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;after selecting a region such as &lt;strong&gt;United Kingdom&lt;/strong&gt;, the dependent filter is refreshed automatically and displays only the corresponding provinces:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnaknzdi26nm56l7esrq0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnaknzdi26nm56l7esrq0.png" alt="Dependant Select List is Refreshed Automatically"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an additional exercise, try making the dependent filter &lt;strong&gt;required&lt;/strong&gt; and configure a display message indicating that a country or region must be selected first.&lt;/p&gt;

&lt;p&gt;So far, all examples in this series have been based on &lt;strong&gt;data tables&lt;/strong&gt; as the underlying data source. However, real-life reporting scenarios often rely on &lt;strong&gt;stored procedures&lt;/strong&gt; and &lt;strong&gt;user-defined functions&lt;/strong&gt;. In the next post, we’ll explore how the same filtering techniques can be applied when working with those types of sources.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>sql</category>
      <category>analytics</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with Seal Report: Static and Dynamic Select Lists</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Thu, 26 Mar 2026 22:33:48 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-static-and-dynamic-select-lists-36o</guid>
      <description>&lt;p&gt;This is the &lt;strong&gt;third post in the series&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the previous post, we created a &lt;strong&gt;pivot table with custom filters&lt;/strong&gt;. While those filters allowed us to apply constraints such as &lt;strong&gt;Equals&lt;/strong&gt;, &lt;strong&gt;Contains&lt;/strong&gt;, and &lt;strong&gt;Between&lt;/strong&gt;, it would be useful to extend the example further by introducing &lt;strong&gt;static and dynamic select lists&lt;/strong&gt; as filtering options.&lt;/p&gt;

&lt;p&gt;In this post, we will enhance the existing solution by integrating these select lists and exploring how they improve the filtering experience.&lt;/p&gt;

&lt;p&gt;Below is the link to the previous post:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Creating a Pivot Table with Custom Filters&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" alt="vladg_dev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3383374" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 22&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" id="article-link-3383374"&gt;
          Getting Started with Seal Report: Creating a Pivot Table with Custom Filters
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;h2&gt;
  
  
  Adding a Static Enumeration
&lt;/h2&gt;

&lt;p&gt;In the context of &lt;strong&gt;Seal Report&lt;/strong&gt;, a &lt;strong&gt;static enumeration&lt;/strong&gt; is a collection of key-value pairs defined directly in the report’s &lt;strong&gt;XML document definition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, instead of retrieving the distinct values of &lt;strong&gt;PayFrequency&lt;/strong&gt; from the &lt;strong&gt;HumanResources.EmployeePayHistory&lt;/strong&gt; table, it is often more efficient to store these values directly in the data model used for rendering the report. Even executing a hardcoded SQL query each time the report runs would be less efficient than using a predefined collection.&lt;/p&gt;

&lt;p&gt;In this case, the static enumeration behaves like a constant list that is already available at runtime and does not require additional database queries.&lt;/p&gt;

&lt;p&gt;Select the &lt;strong&gt;Add Enum&lt;/strong&gt; option available under &lt;strong&gt;Sources → Data Source → Enumerated Lists&lt;/strong&gt;. A contextual configuration panel will appear on the right side.&lt;/p&gt;

&lt;p&gt;Within the enum definition, provide a meaningful &lt;strong&gt;Name&lt;/strong&gt;, and configure the following options if needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;whether the list is &lt;strong&gt;dynamically loaded&lt;/strong&gt; from a &lt;strong&gt;SQL Select Statement&lt;/strong&gt; or from a &lt;strong&gt;Load Script&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;whether the list should be &lt;strong&gt;refreshed before report execution&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;whether the list values &lt;strong&gt;depend on the connection&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These settings allow you to control how enumeration values are loaded and updated during report execution.&lt;/p&gt;

&lt;p&gt;For the current scope, all these options remain set to &lt;strong&gt;False&lt;/strong&gt;, since we are defining a static enumeration. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;SQL Select Statement&lt;/strong&gt; used to define the enumeration values is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Monthly'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Biweekly'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;PayFrequencyEnum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The enum's configuration looks this way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsis6z4ek6l0ivztjodu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsis6z4ek6l0ivztjodu.png" alt="Static Enum Configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note that the &lt;strong&gt;Refresh values&lt;/strong&gt; option may not appear immediately in the UI. If it is not visible, reopen the enum definition or temporarily enable a dynamic loading option so the two values become available in the final collection.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The enumeration created in the UI is persisted in the report definition as a &lt;code&gt;&amp;lt;MetaEnum&amp;gt;&lt;/code&gt; element. A simplified example looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;MetaEnum&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;GUID&amp;gt;&lt;/span&gt;a90b0ce6-bde8-4199-8869-f2ce0c1218ce&lt;span class="nt"&gt;&amp;lt;/GUID&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;PayrollFrequency&lt;span class="nt"&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Sql&amp;gt;&lt;/span&gt;SELECT *
FROM (VALUES
    (CAST(1 AS INT), 'Monthly'),
    (CAST(2 AS INT), 'Biweekly')
) AS PayFrequencyEnum([Key], [Value])&lt;span class="nt"&gt;&amp;lt;/Sql&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Values&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;MetaEV&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Id&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/Id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Val&amp;gt;&lt;/span&gt;Monthly&lt;span class="nt"&gt;&amp;lt;/Val&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/MetaEV&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;MetaEV&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Id&amp;gt;&lt;/span&gt;2&lt;span class="nt"&gt;&amp;lt;/Id&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Val&amp;gt;&lt;/span&gt;Biweekly&lt;span class="nt"&gt;&amp;lt;/Val&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/MetaEV&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/Values&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/MetaEnum&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the enumeration is configured as &lt;strong&gt;static&lt;/strong&gt;, the actual values used at runtime are stored inside the &lt;code&gt;&amp;lt;Values&amp;gt;&lt;/code&gt; section. In this case, the &lt;code&gt;&amp;lt;Sql&amp;gt;&lt;/code&gt; element is not required for execution and mainly serves as a definition template. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can even remove the &lt;code&gt;&amp;lt;Sql&amp;gt;&lt;/code&gt; element and keep only the &lt;code&gt;&amp;lt;Values&amp;gt;&lt;/code&gt; collection to preserve the same static behavior.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a Dynamic Enumeration
&lt;/h2&gt;

&lt;p&gt;For a &lt;strong&gt;dynamic enumeration&lt;/strong&gt;, let’s select a set of properties directly from the database that are expected to change over time.&lt;/p&gt;

&lt;p&gt;For example, the &lt;strong&gt;HumanResources.EmployeeDepartmentHistory&lt;/strong&gt; table references the &lt;strong&gt;HumanResources.Shift&lt;/strong&gt; table. This means that the available shift values may change in the future as new shifts are added or existing ones are modified.&lt;/p&gt;

&lt;p&gt;In such scenarios, using a &lt;strong&gt;dynamic enumeration&lt;/strong&gt; ensures that the list of available values is always loaded from the database and reflects the current state of the data.&lt;/p&gt;

&lt;p&gt;For this particular case, the list will be &lt;strong&gt;dynamically loaded from the SQL Select Statement&lt;/strong&gt;, and its values will &lt;strong&gt;depend on the connection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I've chosen the following &lt;strong&gt;SQL Select Statement&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ShiftID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;CONCAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
        &lt;span class="s1"&gt;' ('&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;FORMAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;StartTime&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;datetime2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'HH:mm'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;' - '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;FORMAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;EndTime&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;datetime2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'HH:mm'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;')'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AdventureWorks2025&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;HumanResources&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;Shift&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration looks the following way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5d1mrwgzaflx521bgeu7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5d1mrwgzaflx521bgeu7.png" alt="Dynamic Enum Configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The enumeration will be reflected in the report definition like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;MetaEnum&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;GUID&amp;gt;&lt;/span&gt;c107bf33-5f3a-4f26-908d-48a8f768d0d9&lt;span class="nt"&gt;&amp;lt;/GUID&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;Shifts&lt;span class="nt"&gt;&amp;lt;/Name&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;IsDynamic&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/IsDynamic&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;ValuesPerConnection&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/ValuesPerConnection&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Sql&amp;gt;&lt;/span&gt;SELECT 
    [ShiftID] AS [KEY],
    CONCAT(
        [Name], 
        ' (',
        FORMAT(CAST([StartTime] AS datetime2), 'HH:mm'),
        ' - ',
        FORMAT(CAST([EndTime] AS datetime2), 'HH:mm'),
        ')'
    ) AS [VALUE]
FROM [AdventureWorks2025].[HumanResources].[Shift]&lt;span class="nt"&gt;&amp;lt;/Sql&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/MetaEnum&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the report definition may also include a key–value collection inside a &lt;code&gt;&amp;lt;Values&amp;gt;&lt;/code&gt; section. This typically appears after selecting &lt;strong&gt;Refresh values&lt;/strong&gt; or pressing &lt;strong&gt;F9&lt;/strong&gt;, which materializes the current enumeration entries in the report model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying Enumerations on Restrictions
&lt;/h2&gt;

&lt;p&gt;The process of applying a &lt;strong&gt;select-based filter&lt;/strong&gt; is very similar to the one used for expression-based filters. The key difference is that, within the &lt;strong&gt;Advanced&lt;/strong&gt; section, you must select the appropriate &lt;strong&gt;Custom enum&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Drag and drop the &lt;strong&gt;PayFrequency&lt;/strong&gt; property from the &lt;strong&gt;HumanResources.EmployeePayHistory&lt;/strong&gt; table into the &lt;strong&gt;Restrictions&lt;/strong&gt; section. Then click on the newly created &lt;strong&gt;[PayFrequency Equals]&lt;/strong&gt; expression. In the contextual panel on the right side, navigate to the &lt;strong&gt;Advanced&lt;/strong&gt; section and select the &lt;strong&gt;PayrollFrequency&lt;/strong&gt; enumeration.&lt;/p&gt;

&lt;p&gt;Execute the report, and you will see the available filters as shown in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnm5ez0h25352m2u52xwu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnm5ez0h25352m2u52xwu.png" alt="Mapping Restriction with an Enumeration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It does not matter whether the enumeration is &lt;strong&gt;static&lt;/strong&gt; or &lt;strong&gt;dynamic&lt;/strong&gt; — both are applied in the same way when configuring a restriction or filter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;Apply the &lt;strong&gt;Shifts&lt;/strong&gt; enumeration to the &lt;strong&gt;ShiftID&lt;/strong&gt; property from the &lt;strong&gt;HumanResources.Shift&lt;/strong&gt; table. The process is similar to the one discussed in the previous section.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hint: combine restrictions using both the &lt;strong&gt;PayrollFrequency&lt;/strong&gt; and &lt;strong&gt;Shifts&lt;/strong&gt; enumerations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you’ve configured it successfully, try answering the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How many employees from the &lt;strong&gt;Engineering&lt;/strong&gt; department have agreed to a &lt;strong&gt;Biweekly&lt;/strong&gt; payroll frequency and are working the &lt;strong&gt;Day shift (07:00 – 15:00)&lt;/strong&gt;?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How many employees with a &lt;strong&gt;FirstName&lt;/strong&gt; starting with &lt;strong&gt;Chris&lt;/strong&gt; are assigned to either the &lt;strong&gt;Evening&lt;/strong&gt; or &lt;strong&gt;Night&lt;/strong&gt; shift?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dependent select lists allow one enumeration to react dynamically to the selection of another. In one of the following posts, I will show you how to apply &lt;strong&gt;dependent select lists&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Summary
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Static vs Dynamic Enumerations
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Static&lt;/th&gt;
&lt;th&gt;Dynamic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Loaded at runtime&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Requires SQL execution&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stored in &lt;code&gt;&amp;lt;Values&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reflects DB changes automatically&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Depends on connection&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use static enumerations for constant lookup lists and dynamic enumerations when values must reflect the current database state.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>tutorial</category>
      <category>analytics</category>
      <category>sqlserver</category>
    </item>
    <item>
      <title>Getting Started with Seal Report: Creating a Pivot Table with Custom Filters</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Sun, 22 Mar 2026 12:28:42 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-creating-a-pivot-table-with-custom-filters-44bo</guid>
      <description>&lt;p&gt;This is the second post in the series.&lt;/p&gt;

&lt;p&gt;In this article, I assume that you’re already familiar with how to &lt;strong&gt;configure a data source&lt;/strong&gt; in &lt;strong&gt;Seal Report&lt;/strong&gt; and how to set up &lt;strong&gt;metadata model elements&lt;/strong&gt; from a table loaded via the &lt;strong&gt;catalog&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can find the previous post here:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Seal Report: Building Your First Data Table from SQL Server&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vladg_dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" alt="vladg_dev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vladg_dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vlad Ganușceac
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vlad Ganușceac
                
              
              &lt;div id="story-author-preview-content-3381026" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vladg_dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834141%2Fcf0348a9-b9e3-4aae-9580-40f14c497a0c.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vlad Ganușceac&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 21&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" id="article-link-3381026"&gt;
          Getting Started with Seal Report: Building Your First Data Table from SQL Server
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sealreport"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sealreport&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/analytics"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;analytics&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sqlserver"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sqlserver&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;The previous report used only out-of-the-box features. In this tutorial, we will extend it by adding a &lt;strong&gt;pivot table&lt;/strong&gt; and introducing &lt;strong&gt;custom filters&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a Pivot Table
&lt;/h2&gt;

&lt;p&gt;Since the current report visualizes data from the &lt;strong&gt;HumanResources.Employee&lt;/strong&gt; and &lt;strong&gt;Person.Person&lt;/strong&gt; tables of the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; database, we need to use a &lt;strong&gt;one-to-many relationship&lt;/strong&gt; with one of these tables to provide data suitable for a pivot table.&lt;/p&gt;

&lt;p&gt;Let’s drag the &lt;strong&gt;Rate&lt;/strong&gt; property from &lt;strong&gt;HumanResources.EmployeePayHistory&lt;/strong&gt; into the &lt;strong&gt;Drop Data Elements&lt;/strong&gt; section of the &lt;strong&gt;metadata model&lt;/strong&gt;. Then drag the &lt;strong&gt;PayFrequency&lt;/strong&gt; property from the same table into the &lt;strong&gt;Drop Column Elements&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;The final configuration will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4iprf0vtwpav3bki28no.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4iprf0vtwpav3bki28no.png" alt="Configuring Pivot Table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Execute&lt;/strong&gt; button to see the result.&lt;/p&gt;

&lt;p&gt;Now, in addition to the static columns, a list of payment rates grouped by frequency will appear.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hku1r9qrl93gxqxf149.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hku1r9qrl93gxqxf149.png" alt="Pivot Table Sample"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that when you click the &lt;strong&gt;Rate&lt;/strong&gt; property inside the &lt;strong&gt;Drop Data Elements&lt;/strong&gt; section, a context panel appears on the right side. There you can configure several important data options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Aggregate&lt;/strong&gt; (default: &lt;strong&gt;Sum&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculation option&lt;/strong&gt; (disabled by default)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Show total&lt;/strong&gt; (disabled by default)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These settings control how numeric values are calculated and displayed in the pivot output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Custom Filters
&lt;/h2&gt;

&lt;p&gt;Besides the default multi-column search available in the data table view, it is useful to add the ability to filter data using different expressions per column.&lt;/p&gt;

&lt;p&gt;In this context, &lt;em&gt;filters&lt;/em&gt; refer to the &lt;strong&gt;Restrictions and Aggregate Restrictions&lt;/strong&gt; section located at the bottom of the &lt;strong&gt;Models → Model&lt;/strong&gt; configuration panel.&lt;/p&gt;

&lt;p&gt;These restrictions allow you to define parameterized filters that can be reused across report views and applied before the data is rendered.&lt;/p&gt;

&lt;p&gt;Let’s drag and drop into the &lt;strong&gt;Restrictions and Aggregate Restrictions&lt;/strong&gt; section the same properties that were previously added to the &lt;strong&gt;Row Elements&lt;/strong&gt; of the metadata model.&lt;/p&gt;

&lt;p&gt;For each restriction, enable the &lt;strong&gt;Prompt at execution&lt;/strong&gt; option in the &lt;strong&gt;Prompt restriction&lt;/strong&gt; setting so that users can specify filter values when running the report.&lt;/p&gt;

&lt;p&gt;Your configuration should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsri847ekdk4hgmqbfq7f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsri847ekdk4hgmqbfq7f.png" alt="Configuring Filters for Data Table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Execute&lt;/strong&gt; button. In the new window, the newly added filters will appear. These filters are optional.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Execute&lt;/strong&gt; again in that window to run the report.&lt;/p&gt;

&lt;p&gt;Try applying different combinations of filters. The results displayed in the data table will change depending on the selected filter values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wvt0hq4jfju6vqak996.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wvt0hq4jfju6vqak996.png" alt="Applying Different Combination of Filters/Restrictions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;So far, we have followed the approach &lt;em&gt;"you filter what you see"&lt;/em&gt;, meaning that restrictions were added only for the fields already present in the report model.&lt;/p&gt;

&lt;p&gt;Try adding the &lt;strong&gt;Name&lt;/strong&gt; field from &lt;strong&gt;HumanResources.Department&lt;/strong&gt; table as &lt;strong&gt;Department&lt;/strong&gt; restriction with default value set to &lt;strong&gt;Engineering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some readers might wonder why we are not using &lt;strong&gt;select lists&lt;/strong&gt; yet, especially since SQL-based lists better represent real-life filtering scenarios than simple column-based restrictions.&lt;/p&gt;

&lt;p&gt;In the next article, we will configure &lt;strong&gt;dynamic SQL-based select-list filters&lt;/strong&gt;, making restrictions more flexible and closer to real-world reporting scenarios.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>tutorial</category>
      <category>sqlserver</category>
      <category>analytics</category>
    </item>
    <item>
      <title>Getting Started with Seal Report: Building Your First Data Table from SQL Server</title>
      <dc:creator>Vlad Ganușceac</dc:creator>
      <pubDate>Sat, 21 Mar 2026 17:54:42 +0000</pubDate>
      <link>https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g</link>
      <guid>https://dev.to/vladg_dev/getting-started-with-seal-report-building-your-first-data-table-from-sql-server-140g</guid>
      <description>&lt;p&gt;The first time I was assigned a task to create a Seal Report for data visualization, I quickly discovered there were very few posts, articles, or practical tutorials explaining how to accomplish common tasks. The official &lt;code&gt;.srex&lt;/code&gt; XML-based report definitions are available inside the Seal Report Designer, but in many cases the experience still feels like &lt;em&gt;"figure it out on your own"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Seal Report is an open-source reporting framework for .NET that allows building dynamic reports from SQL data sources with minimal configuration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The motivation behind this post is to provide a starting point for developers who are struggling to find clear guidance on how to work efficiently with Seal Report.&lt;/p&gt;

&lt;p&gt;In this short tutorial, I will use the &lt;strong&gt;AdventureWorks2025&lt;/strong&gt; open-source backup database. &lt;em&gt;Make sure the database is restored in your SQL Server instance before starting the tutorial.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All examples described below are executed using &lt;strong&gt;Seal Report Designer (Community Edition)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This tutorial focuses on building a simple working report from scratch using Seal Report Designer and is intended as a starting point for developers who are new to the tool.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Data Source
&lt;/h2&gt;

&lt;p&gt;Below are the steps required to configure a data source that will later be used for data visualization:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Seal Report Designer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;New&lt;/strong&gt; icon&lt;/li&gt;
&lt;li&gt;In the left panel, right-click the &lt;strong&gt;Sources&lt;/strong&gt; folder&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Add a new SQL Data Source&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After completing these steps, a &lt;strong&gt;dummy SQL data source&lt;/strong&gt; will be created.&lt;/p&gt;

&lt;p&gt;For simplicity, you can delete the default data sources and repositories created automatically by Seal Report, since they are not required for this tutorial. Keeping only the necessary elements makes the configuration easier to follow and avoids confusion later.&lt;/p&gt;

&lt;p&gt;At this point, your workspace should look similar to the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxj5odadlyklhpd7lypx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxj5odadlyklhpd7lypx9.png" alt="Added Dummy Data Source" width="188" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To connect the data source to an existing database, navigate to: &lt;strong&gt;Sources&lt;/strong&gt; → &lt;strong&gt;Data Source&lt;/strong&gt; → &lt;strong&gt;Connections&lt;/strong&gt; → &lt;strong&gt;connection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After selecting &lt;strong&gt;connection&lt;/strong&gt;, the contextual configuration panel becomes available on the right side.&lt;/p&gt;

&lt;p&gt;For this tutorial, use the following settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database type&lt;/strong&gt;: MS SQLServer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection type&lt;/strong&gt;: MS SQLServer (System.Data)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection string&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data Source=localhost\SQLEXPRESS;
Initial Catalog=AdventureWorks2025;
Integrated Security=True;
TrustServerCertificate=True;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the connection string is configured, press &lt;strong&gt;F7&lt;/strong&gt; or click &lt;strong&gt;Check connection&lt;/strong&gt; to verify that the connection is working correctly.&lt;/p&gt;

&lt;p&gt;If everything is configured properly, the result should match the confirmation shown in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefcavw6quslua8gbjawg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefcavw6quslua8gbjawg.png" alt="Configured Database Connection on Data Source" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading Tables and Views from the Catalog
&lt;/h2&gt;

&lt;p&gt;After the database connection is established, tables and views can be loaded from the database catalog for further use in the report model.&lt;/p&gt;

&lt;p&gt;Open &lt;strong&gt;Sources → Data Source → Tables&lt;/strong&gt;, then right-click &lt;strong&gt;Tables&lt;/strong&gt; and select &lt;strong&gt;Add Tables from Catalog&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the popup window, enter &lt;strong&gt;HumanResources&lt;/strong&gt; in the filter field and click Select all. Enable the following mapping options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Auto create joins&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auto create table columns&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use schema name&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep column names&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These options automatically prepare the selected tables for reporting by generating joins between them and creating the required column metadata.&lt;/p&gt;

&lt;p&gt;The result should look similar to the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcszuja1yslee6b9jrc5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcszuja1yslee6b9jrc5t.png" alt="Importing Tables and Views from the Catalog" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Metadata Model
&lt;/h2&gt;

&lt;p&gt;Inside the &lt;strong&gt;Models&lt;/strong&gt; folder, there is already a metadata &lt;strong&gt;Model&lt;/strong&gt; element created by default. Select it and, in the contextual panel on the right side, set the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt; → &lt;strong&gt;Data Source&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection&lt;/strong&gt; → &lt;strong&gt;connection&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the bottom of the same contextual panel, you will see the list of tables imported earlier from the catalog. Expand the &lt;strong&gt;HumanResources.Employee&lt;/strong&gt; table and drag the following fields into the &lt;strong&gt;Drop Row Element&lt;/strong&gt; area:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;BusinessEntityID&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JobTitle&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BirthDate&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MaritalStatus&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gender&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this stage, before previewing the result, your configuration should look similar to the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75b142i7zko6odb95iyz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75b142i7zko6odb95iyz.png" alt="Configuring Table Columns for Visualization" width="800" height="818"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By clicking the &lt;strong&gt;Execute&lt;/strong&gt; button, the data is loaded from the database, mapped to the metadata model, and rendered in the UI using the Razor engine.&lt;/p&gt;

&lt;p&gt;As you can see, by default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all columns are sortable&lt;/li&gt;
&lt;li&gt;pagination is enabled&lt;/li&gt;
&lt;li&gt;a global table filter is available&lt;/li&gt;
&lt;li&gt;the maximum number of records per page can be adjusted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms2lutpfa0gzs0pc6111.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms2lutpfa0gzs0pc6111.png" alt="A Showcase of paginated sortable and filterable data visualization in tabular form" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These features are available out of the box and do not require any additional configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skill Check
&lt;/h2&gt;

&lt;p&gt;You may notice that displaying &lt;strong&gt;BusinessEntityID&lt;/strong&gt; instead of a person’s full name looks awkward. As a small exercise, try adding the &lt;strong&gt;Person.Person&lt;/strong&gt; table from the catalog and replacing the identifier with the &lt;strong&gt;FirstName&lt;/strong&gt;, &lt;strong&gt;MiddleName&lt;/strong&gt;, and &lt;strong&gt;LastName&lt;/strong&gt; fields.&lt;/p&gt;

&lt;p&gt;If everything is configured correctly, the resulting data table should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjyfooc009ib6v5ama3k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjyfooc009ib6v5ama3k.png" alt="Automatic join between 2 tables on data visualization" width="800" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next post, I will show how to apply custom filters and how to build a pivot table.&lt;/p&gt;

</description>
      <category>sealreport</category>
      <category>opensource</category>
      <category>analytics</category>
      <category>sqlserver</category>
    </item>
  </channel>
</rss>
