Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The following may or may not be the right approach for your specific situation, but it can be a useful technique to have at your disposal.</p> <p>You can add some code to your select statement to help yourself answer questions like these 'downstream' (either via added criteria or via SSRS). See <a href="http://sqlfiddle.com/#!3/27b91/5/0" rel="nofollow">this modification of your SQL Fiddle</a>:</p> <pre><code>select pid, firstName, lastName, dateOfBirth, obsName, obsValue, obsDate, rowRank, CASE rowRank WHEN 1 THEN 1 ELSE 0 END AS countableRow from ( select Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth , Obs.obsName, Obs.obsValue, Obs.obsDate, ROW_NUMBER() OVER (PARTITION BY Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth ORDER BY Obs.obsDate) AS rowRank from Person join Obs on Person.pId = Obs.pId ) rankedData </code></pre> <p>The <code>rowRank</code> field will create a group-relative ranking number, which may or may not be useful to you downstream. The <code>countableRow</code> field will be either 1 or 0 such that each group will have one and only one row with a 1 in it. Doing <code>SUM(countableRow)</code> will give you the proper number of groups in your data.</p> <p>Now, you can extend this functionality (if you wish) by dumping out actual field values instead of a constant scalar like 1 in the first row of each group. So, if you had <code>CASE rowRank WHEN 1 THEN dateOfBirth ELSE NULL END AS countableDOB</code>, you could then, for example, get the total number of people with each distinct birthday using just this dataset.</p> <p>Of course, you can do all those things using methods like @Russell's with SQL anyway, so this would be most relevant with specific downstream requirements that may not match your situation.</p> <p><strong>EDIT</strong></p> <p>Obviously the countableRow field there isn't a one-size-fits-all solution to the types of queries you want. I have added a few more examples of the <code>PARTITION BY</code> strategy to <a href="http://sqlfiddle.com/#!3/27b91/10" rel="nofollow">another SQL Fiddle</a>:</p> <pre><code>select pid, firstName, lastName, dateOfBirth, obsName, obsValue, obsDate, rowRank, CASE rowRank WHEN 1 THEN 1 ELSE 0 END AS countableRow, valueRank, CASE valueRank WHEN 1 THEN 1 ELSE 0 END AS valueCount, dobRank, CASE WHEN dobRank = 1 AND dateOfBirth IS NOT NULL THEN 1 ELSE 0 END AS dobCount from ( select Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth , Obs.obsName, Obs.obsValue, Obs.obsDate, ROW_NUMBER() OVER (PARTITION BY Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth ORDER BY Obs.obsDate) AS rowRank, ROW_NUMBER() OVER (PARTITION BY Obs.obsName, Obs.obsValue ORDER BY Obs.obsDate) AS valueRank, ROW_Number() OVER (PARTITION BY Person.dateOfBirth ORDER BY Person.pid) AS dobRank from Person join Obs on Person.pId = Obs.pId ) rankedData </code></pre> <p>Lest anyone misunderstand me as suggesting this is always appropriate, it obviously isn't. This isn't a better solution to getting specific answers using additional SQL queries. What it allows you to do is encode enough information to simply answer such questions in the consuming code all in a single result set. That's where it can come in handy.</p> <p><strong>SECOND EDIT</strong></p> <p>Since you were wondering whether you can do this if race data is stored in more than one place, the answer is, absolutely. I have revised the code from my previous SQL Fiddle, which is now <a href="http://sqlfiddle.com/#!3/898b4/6" rel="nofollow">available in a new one</a>:</p> <pre><code>select pid, firstName, lastName, dateOfBirth, obsName, obsValue, obsDate, rowRank, CASE rowRank WHEN 1 THEN 1 ELSE 0 END AS countableRow, valueRank, CASE valueRank WHEN 1 THEN 1 ELSE 0 END AS valueCount, dobRank, CASE WHEN dobRank = 1 AND dateOfBirth IS NOT NULL THEN 1 ELSE 0 END AS dobCount, raceRank, CASE WHEN raceRank = 1 AND (race IS NOT NULL OR obsName = 'RACE') THEN 1 ELSE 0 END AS raceCount from ( select Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth, Person.[race] , Obs.obsName, Obs.obsValue, Obs.obsDate, ROW_NUMBER() OVER (PARTITION BY Person.pid, Person.firstName, Person.lastName, Person.dateOfBirth ORDER BY Obs.obsDate) AS rowRank, ROW_NUMBER() OVER (PARTITION BY Obs.obsName, Obs.obsValue ORDER BY Obs.obsDate) AS valueRank, ROW_NUMBER() OVER (PARTITION BY Person.dateOfBirth ORDER BY Person.pid) AS dobRank, ROW_NUMBER() OVER (PARTITION BY ISNULL(Person.race, CASE Obs.obsName WHEN 'RACE' THEN Obs.obsValue ELSE NULL END) ORDER BY Person.pid) AS raceRank from Person left join Obs on Person.pId = Obs.pId ) rankedData </code></pre> <p>As you can see, in the new Fiddle, this properly counts the number of Races as 3, with 2 being in the Obs table and the third being in the Person table. The trick is that <code>PARTITION BY</code> can contain expressions, not just raw column output. Note that I changed the join to a left join here, and that we need to use a CASE to only include obsValue WHERE obsName is 'RACE'. It is a little complicated, but not overwhelmingly so, and it handles even fairly complex cases gracefully.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload