{"id":301,"date":"2007-08-29T00:00:00","date_gmt":"2007-08-24T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/the-puzzle-of-rating-decomposition\/"},"modified":"2021-09-29T16:22:16","modified_gmt":"2021-09-29T16:22:16","slug":"the-puzzle-of-rating-decomposition","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/the-puzzle-of-rating-decomposition\/","title":{"rendered":"The Puzzle of &#8216;Rating Decomposition&#8217;"},"content":{"rendered":"<p>When you are planning to buy a camera or to read an article, you will always face a dilemma: how will you find the best product from the available products on the market or what article should you pick for reading from a variety of different electronic journals?<\/p>\n<p>Usually, in order to make an informed choice, people start looking for additional information about the product, such as consumer reviews or ratings. This strategy works pretty well and gives the person an idea about the product&#8217;s quality. However, the average rating can be ambiguous and can indicate slightly different things.<\/p>\n<p>For instance, if a camera has a rating of 4.15 out of 5 and has a sufficient number of votes, you might suppose that most people think that the quality of the camera is above average, but not excellent. However, it might be that most people gave camera the highest rating, but a few people found the location of the control buttons inconvenient and too dissimilar to their old cameras and gave the camera a very low mark, so dragging down the average rating.<\/p>\n<p>An average rating on its own does not give you the full information about the product. If you knew how many points each separate voter gave to the camera, the information could be more precise and your decision of what type to buy could be much easier.<\/p>\n<p>But, how do you get that information, if you only know the average rating and the number of votes? Well, you might be surprised to learn that you can figure it out using SQL. <code><\/code><\/p>\n<p>Before we start, create and load an auxiliary table that stores only sequential numbers (see Listing1):<\/p>\n<p><b>Listing1. Create and Load an Auxiliary Table<\/b><\/p>\n<div>\n<p>SET&#160;NOCOUNT&#160;ON; <br \/>DECLARE&#160;&#160;&#160;&#160;@i&#160;INT,&#160;@maxi&#160;INT; <br \/>SELECT&#160;@i&#160;=&#160;0,&#160;@maxi&#160;=&#160;1000; <br \/>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;dbo.sequence&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;U&#8217;)&#160; <br \/>DROP&#160;TABLE&#160;dbo.sequence; <br \/>CREATE&#160;TABLE&#160;dbo.sequence(num&#160;INT&#160;NOT&#160;NULL); <br \/>WHILE(@i&#160;&lt;=&#160;@maxi)&#160; <br \/>&#160;&#160;&#160;BEGIN <br \/>&#160;&#160;&#160;&#160;&#160;&#160;INSERT&#160;INTO&#160;dbo.sequence&#160;VALUES(@i); <br \/>&#160;&#160;&#160;&#160;&#160;&#160;SET&#160;@i&#160;=&#160;@i&#160;+&#160;1;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br \/>&#160;&#160;&#160;END<\/p>\n<\/div>\n<h4>Step1: Build Subsets of Possible Marks <\/h4>\n<p>We need to build a framework that will help us to find and examine the possible readers&#8217; votes. Our first step will be to find the subsets of possible marks<\/p>\n<p>Let&#8217;s start with the number of votes, which on its own can give you a lot of information.<\/p>\n<p>For example, if 15 readers voted for the article, then any of the following predicates can be true:<\/p>\n<ul>\n<li>0 readers rated the article 1 out of 5;  <\/li>\n<li>1 reader rated the article 1 out of 5;  <\/li>\n<li>2 readers rated the article 1 out of 5;  <\/li>\n<li>. . . . . . . . . . . . . . . . . ;  <\/li>\n<li>15 readers rated the article 1 out 5;  <\/li>\n<li>0 readers rated the article 2 out of 5;  <\/li>\n<li>1 reader rated the article 2 out of 5;  <\/li>\n<li>2 readers rated the article 2 out of 5;  <\/li>\n<li>. . . . . . . . . . . . . . . . . ;  <\/li>\n<li>15 readers rated the article 2 out of 5;  <\/li>\n<li>0 readers rated the article 3 out of 5;  <\/li>\n<li>. . . . . . . . . . . . . . . . . ; <\/li>\n<\/ul>\n<p>And so on.<\/p>\n<p>If you translate this logic into T-SQL, you will get five subsets of possible marks. The code in Listing2 shows haw you can build the subsets;<\/p>\n<p><b>Listing2. Build Subsets of Possible Marks<\/b><\/p>\n<div>\n<p>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;spu_createVoteTables&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;P&#8217;)&#160; <br \/>DROP&#160;PROCEDURE&#160;spu_createVoteTables; <br \/>GO <br \/>CREATE&#160;PROCEDURE&#160;spu_createVoteTables&#160;&#160; <br \/>&#160;&#160;&#160;@numVotes&#160;INT <br \/>AS <br \/>&#8211;&#160;create&#160;and&#160;load&#160;table&#160;for&#160;OneS <br \/>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;dbo.OneS&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;U&#8217;)&#160; <br \/>DROP&#160;TABLE&#160;dbo.OneS; <br \/>CREATE&#160;TABLE&#160;dbo.OneS <br \/>&#160;&#160;&#160;(point&#160;INT&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;votes&#160;INT&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;sumPoints&#160;dec(8,4)&#160;NOT&#160;NULL); <\/p>\n<p>INSERT&#160;INTO&#160;dbo.OneS&#160; <br \/>SELECT&#160;1,&#160;num,&#160;1*num&#160;FROM&#160;sequence&#160;WHERE&#160;num&#160;&lt;=&#160;@numVotes; <br \/>SELECT&#160;*&#160;FROM&#160;dbo.OneS; <\/p>\n<p>&#8211;&#160;create&#160;and&#160;load&#160;table&#160;for&#160;TwoS <br \/>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;dbo.TwoS&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;U&#8217;)&#160; <br \/>DROP&#160;TABLE&#160;dbo.TwoS; <br \/>CREATE&#160;TABLE&#160;dbo.TwoS <br \/>&#160;&#160;&#160;&#160;&#160;(point&#160;INT&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;votes&#160;INT&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;sumPoints&#160;dec(8,4)&#160;NOT&#160;NULL); <\/p>\n<p>INSERT&#160;INTO&#160;dbo.TwoS&#160; <br \/>SELECT&#160;2,&#160;num,&#160;2*num&#160;FROM&#160;sequence&#160;WHERE&#160;num&#160;&lt;=&#160;@numVotes; <br \/>SELECT&#160;*&#160;FROM&#160;dbo.TwoS <\/p>\n<p>&#8211;&#160;create&#160;and&#160;load&#160;table&#160;for&#160;ThreeS <br \/>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160;<br \/>&#160;&#160; WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;dbo.ThreeS&#8217;))<br \/>&#160;&#160; AND&#160;xtype&#160;=&#160;&#8217;U&#8217;)&#160; <br \/>DROP&#160;TABLE&#160;dbo.ThreeS; <br \/>CREATE&#160;TABLE&#160;dbo.ThreeS <br \/>&#160;&#160;&#160;(point&#160;INT&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;votes&#160;INT&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;sumPoints&#160;dec(8,4)&#160;NOT&#160;NULL); <\/p>\n<p>INSERT&#160;INTO&#160;dbo.ThreeS&#160; <br \/>SELECT&#160;3,&#160;num,&#160;3*num&#160;FROM&#160;sequence&#160;WHERE&#160;num&#160;&lt;=&#160;@numVotes; <br \/>SELECT&#160;*&#160;FROM&#160;dbo.ThreeS <\/p>\n<p>&#8211;&#160;create&#160;and&#160;load&#160;table&#160;for&#160;FourS <br \/>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;dbo.FourS&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;U&#8217;)&#160; <br \/>DROP&#160;TABLE&#160;dbo.FourS; <br \/>CREATE&#160;TABLE&#160;dbo.FourS <br \/>&#160;&#160;&#160;(point&#160;INT&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;votes&#160;INT&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;sumPoints&#160;dec(8,4)&#160;NOT&#160;NULL); <\/p>\n<p>INSERT&#160;INTO&#160;dbo.FourS&#160; <br \/>SELECT&#160;4,&#160;num,&#160;4*num&#160;FROM&#160;sequence&#160;WHERE&#160;num&#160;&lt;=&#160;@numVotes; <br \/>SELECT&#160;*&#160;FROM&#160;dbo.FourS <\/p>\n<p>&#8211;&#160;create&#160;and&#160;load&#160;table&#160;for&#160;FiveS <br \/>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;dbo.FiveS&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;U&#8217;)&#160; <br \/>DROP&#160;TABLE&#160;dbo.FiveS; <br \/>CREATE&#160;TABLE&#160;dbo.FiveS <br \/>&#160;&#160;&#160;(point&#160;INT&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;votes&#160;INT&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;sumPoints&#160;dec(8,4)&#160;NOT&#160;NULL); <\/p>\n<p>INSERT&#160;INTO&#160;dbo.FiveS&#160; <br \/>SELECT&#160;5,&#160;num,&#160;5*num&#160;FROM&#160;sequence&#160;WHERE&#160;num&#160;&lt;=&#160;@numVotes; <br \/>SELECT&#160;*&#160;FROM&#160;dbo.FiveS <br \/>GO <br \/>EXEC&#160;spu_createVoteTables&#160;@numVotes&#160;=&#160;15<\/p>\n<\/div>\n<p>The results look like this:<\/p>\n<div>\n<p>point&#160;&#160;&#160;&#160;&#160;&#160; votes&#160;&#160;&#160;&#160;&#160;&#160; sumPoints<br \/>&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;-<br \/>1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0.0000<br \/>1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1.0000<br \/>1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 2&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 2.0000<br \/>.&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .<br \/>1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 15.0000<\/p>\n<p>point&#160;&#160;&#160;&#160;&#160;&#160; votes&#160;&#160;&#160;&#160;&#160;&#160; sumPoints<br \/>&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;-<br \/>2&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0.0000<br \/>.&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .<br \/>2&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 30.0000<\/p>\n<p>point&#160;&#160;&#160;&#160;&#160;&#160; votes&#160;&#160;&#160;&#160;&#160;&#160; sumPoints<br \/>&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;-<br \/>3&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;0.0000<br \/>.&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .<br \/>3&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 45.0000<\/p>\n<p>point&#160;&#160;&#160;&#160;&#160;&#160; votes&#160;&#160;&#160;&#160;&#160;&#160; sumPoints<br \/>&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;-<br \/>4&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0.0000<br \/>4&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>.&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .<br \/>4&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 60.0000<\/p>\n<p>point&#160;&#160;&#160;&#160;&#160;&#160; votes&#160;&#160;&#160;&#160;&#160;&#160; sumPoints<br \/>&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;-<br \/>5&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0.0000<br \/>5&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 5.0000<br \/>.&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .<br \/>5&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 14&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 70.0000<br \/>5&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 75.0000<\/p>\n<\/div>\n<p>Notice that the number of generated sets (tables) is equal to five and the number of rows in each table is equal to 15.<\/p>\n<p>In general, the number of sets (tables) is equal to a base of the voting system (for the base-5 voting system, the number of tables will be five). The number of rows in each table is equal to the number of votes. <\/p>\n<p>Since each magazine, electronic journal or consumers&#8217; website has its own voting system and that system can change very rarely, the number of the tables will be a static component of our framework. Each time when we analyze the new rating, we will reload these tables. Each time the number of rows in the tables will be equal to the number of votes that produced the rating.<\/p>\n<p>Throughout the rest of this article, we will consider the base-5 voting system only.<\/p>\n<h4>Step2: Find Possible Total Points <\/h4>\n<p>Generally, calculating averages is a very simple task. All you need to do is to find the sum of all attends and then divide that total by the number of attends. The result of division then needs to be rounded to a whole number or to one (two) digits after the decimal point.<\/p>\n<p>Our task, however, is different. We will try the inverse operation: calculating the possible sum total(s) for a known average and known number of attends.<\/p>\n<p>Consider the next example:<\/p>\n<p>You are going to read an article that has a rating of 4 when the number of votes is 15. Therefore, the total score will be 4*15=60. However, 60 is not the only possible total. Taking any number between 53 and 67, dividing by 15, and then rounding the result to a whole number, will give an average of 4.<\/p>\n<p>Therefore, the inverse operation brings multiple results and, as you will see later, depends on the precision that was used to calculate the average.<\/p>\n<p>The following script (Listing3) shows how to find all of the possible totals for the given rating and number of votes. <\/p>\n<p>Listing3. Find Possible Total Points<\/p>\n<div>\n<p>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;spu_findPossibleTotals&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;P&#8217;)&#160; <br \/>DROP&#160;PROCEDURE&#160;spu_findPossibleTotals; <br \/>GO <br \/>CREATE&#160;PROCEDURE&#160;spu_findPossibleTotals&#160;&#160; <br \/>&#160;&#160;&#160;@rating&#160;dec(8,4),&#160; <br \/>&#160;&#160;&#160;@numVotes&#160;dec(8,4), <br \/>&#160;&#160;&#160;@precision&#160;INT <br \/>AS <br \/>SET&#160;NOCOUNT&#160;ON; <br \/>DECLARE&#160;@calcPoints&#160;dec(8,4),&#160;@calcRating&#160;dec(8,4); <\/p>\n<p>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;possiblePoints&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;U&#8217;)&#160; <br \/>DROP&#160;TABLE&#160;possiblePoints; <br \/>CREATE&#160;TABLE&#160;possiblePoints <br \/>&#160;&#160;&#160;(TotalPoints&#160;dec(8,4)&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;calcRaiting&#160;dec(8,4)&#160;NOT&#160;NULL, <br \/>&#160;&#160;&#160;&#160;&#160;roundedRating&#160;dec(8,4)&#160;NOT&#160;NULL) <\/p>\n<p>SELECT&#160;@calcPoints&#160;=&#160;ROUND(@rating&#160;*&#160;@numVotes,&#160;0); <br \/>SELECT&#160;@calcRating&#160;=&#160;@rating <br \/>WHILE(ROUND(@calcRating,@precision)&#160;=&#160;@rating) <br \/>BEGIN <br \/>&#160;&#160;&#160;INSERT&#160;INTO&#160;possiblePoints&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;VALUES(@calcPoints,&#160;@calcRating,&#160;ROUND(@calcRating,@precision)); <br \/>&#160;&#160;&#160;SELECT&#160;@calcPoints&#160;=&#160;@calcPoints&#160;+&#160;1; <br \/>&#160;&#160;&#160;SELECT&#160;@calcRating&#160;=&#160;@calcPoints\/@numVotes; <br \/>END <\/p>\n<p>SELECT&#160;@calcPoints&#160;=&#160;MIN(TotalPoints)&#160;-&#160;1&#160;FROM&#160;possiblePoints <br \/>SELECT&#160;@calcRating&#160;=&#160;@calcPoints\/@numVotes <br \/>WHILE(ROUND(@calcRating,@precision)&#160;=&#160;@rating) <br \/>BEGIN <br \/>&#160;&#160;&#160;INSERT&#160;INTO&#160;possiblePoints&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;VALUES(@calcPoints,&#160;@calcRating,&#160;ROUND(@calcRating,@precision)); <br \/>&#160;&#160;&#160;SELECT&#160;@calcPoints&#160;=&#160;@calcPoints&#160;-&#160;1; <br \/>&#160;&#160;&#160;SELECT&#160;@calcRating&#160;=&#160;@calcPoints\/@numVotes; <br \/>END <br \/>SELECT&#160;*&#160;FROM&#160;possiblePoints&#160;ORDER&#160;BY&#160;TotalPoints <br \/>GO<\/p>\n<\/div>\n<p>If you test the procedure for @rating = 4, @numVotes = 15 and @precision = 0, where precision is the number of digits after the decimal point, you will get the next result:<\/p>\n<div>\n<p>EXEC&#160;spu_findPossibleTotals&#160;@rating&#160;=&#160;4,&#160;@numVotes&#160;=&#160;15,&#160;@precision&#160;=&#160;0 &#160;<\/p>\n<p>TotalPoints&#160;&#160;&#160;&#160; calcRaiting&#160;&#160;&#160;&#160;&#160;&#160; roundedRating<br \/>&#8212;&#8212;&#8212;&#8211;&#160;&#160;&#160;&#160; &#8212;&#8212;&#8212;&#8211;&#160;&#160;&#160;&#160;&#160;&#160; &#8212;&#8212;&#8212;&#8212;-<br \/>53.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 3.5333&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>54.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 3.6000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>55.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 3.6667&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>56.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 3.7333&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>57.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 3.8000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>58.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 3.8667&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;4.0000<br \/>59.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 3.9333&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>60.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>61.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0667&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>62.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.1333&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>63.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.2000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>64.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.2667&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>65.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.3333&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>66.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.4000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>67.0000&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.4667&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000 <\/p>\n<\/div>\n<p>When you change the precision from 0 to 1, you will get only one possible total:<\/p>\n<div>\n<p>EXEC&#160;spu_findPossibleTotals&#160;@rating&#160;=&#160;4,&#160;@numVotes&#160;=&#160;15,&#160;@precision&#160;=&#160;1 &#160;<\/p>\n<p>TotalPoints&#160;&#160;&#160;&#160; calcRaiting&#160;&#160;&#160;&#160;&#160;&#160; roundedRating<br \/>&#8212;&#8212;&#8212;&#8211;&#160;&#160;&#160;&#160; &#8212;&#8212;&#8212;&#8211;&#160;&#160;&#160;&#160;&#160;&#160; &#8212;&#8212;&#8212;&#8212;-<br \/>60.0000&#160;&#160;&#160;&#160; &#160;&#160;&#160; 4.0000&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<\/p>\n<\/div>\n<p>You can test the correctness of the last result very easily:<\/p>\n<p>Add 1 to the result, divide the new sum by 15 and then round the quotient to one digit after the decimal point: (60 + 1) \/ 15 = 4.067 = 4.1. Since 4.1 is greater than the given average of 4.0, the sum of all of the points cannot be 61.<\/p>\n<p>Similarly, (60-1) \/ 15 = 3.93 = 3.9, which is less than the given average 4.0 and then 59 cannot be a possible total as well.<\/p>\n<p>You will see later that the precision of the average (the number of digits after a decimal point), can significantly increase or decrease the entropy of the end result.<\/p>\n<h4>Step3: Remove Invalid Data <\/h4>\n<p>We have now built the framework that we will use to analyze the readers&#8217; votes. However, before we start the actual processing, we need to remove some invalid data from tables <b>OneS<\/b>, <b>TwoS<\/b>, <b>ThreeS<\/b>, <b>FourS<\/b> and <b>FiveS<\/b>.<\/p>\n<p>The following example will explain the logic used to find incorrect data.<\/p>\n<p>Suppose you are going to read an article with an average rating of 4 and a precision of 0, when the number of votes is 15. <\/p>\n<p>Run the procedures that we had previously built (see Lisitng2 and Lisitng3):<\/p>\n<div>\n<p>EXEC&#160;spu_createVoteTables&#160;@numVotes&#160;=&#160;15; <br \/>GO <br \/>EXEC&#160;spu_findPossibleTotals&#160;@rating&#160;=&#160;4,&#160;@numVotes&#160;=&#160;15,&#160;@precision&#160;=&#160;0 <br \/>GO<\/p>\n<\/div>\n<p>The first procedure loads data into the tables <b>OneS<\/b>, <b>TwoS,<\/b> <b>ThreeS<\/b>, <b>FourS<\/b>, <b>FiveS<\/b>. <\/p>\n<p>The second procedure finds all possible totals. For the given rating and the number of votes, they will be in the range from 53 to 67, inclusively.<\/p>\n<p>Now, analyze the data in table OneS. If all 15 readers give article 1 point, then the sum of all marks would be 15 * 1 = 15. Since 15 is smaller than the minimal possible total (which is 53), the situation, when each voter supposedly gave the article 1 point, is incorrect. Therefore, the row where the <b>sumPoints<\/b> column is equal to 15 can be deleted from table OneS.<\/p>\n<p>Similarly, if 14 readers give the article 1 point each, and the 15th reader gives article the highest mark 5, the total would be 14 + 5 = 19, which is still less than minimum possible total. So, the row where the <b>sumPoints<\/b> column is equal to 14, can also be deleted from table <b>OneS.<\/b><\/p>\n<p>Apply the same logic to the rest of the rows of table <b>OneS<\/b> and continue with tables <b>TwoS<\/b>, <b>ThreeS<\/b>, <b>FourS<\/b> and <b>FiveS<\/b>. The number of the rows in all of the tables will be reduced, in some cases significantly.<\/p>\n<p>Now consider a slightly different logic. Consider the case where all 15 readers give the article 5 points each (table <b>FiveS<\/b>). The sum of all of the points would be 15 * 5 = 75, which is greater than the maximum possible total (which is 67). Therefore, the row, where the <b>sumPoints<\/b> column is equal to 75, should be deleted from the table <b>FiveS<\/b>.<\/p>\n<p>If 14 readers give the article 5 points each, the total will be 14 * 5 = 70. The 15th reader can give the article the lowest mark, but this will not change anything: 70 or 71 is greater than maximum possible total (67). The row, where the <b>sumPoints<\/b> column is equal to 70 can be deleted from the table <b>FiveS<\/b>.<\/p>\n<p>However, the case where 13 readers give article 5 points each is valid, because 13 * 5 = 65. If two more readers rate the article 1 out of 5 the total would be 13 * 5 + 2 * 1 = 67, which is a valid total. Therefore, the row, where the <b>sumPoints<\/b> column is equal to 65, should stay in table <b>FiveS<\/b>.<\/p>\n<p>Applying the same logic, you will remove some more rows from tables <b>OneS<\/b>, <b>TwoS<\/b>, <b>ThreeS<\/b> and <b>FourS<\/b>.<\/p>\n<p>Listing4 shows how you could implement the logic that was just explained: <\/p>\n<p><b>Listing4. Remove Invalid Data<\/b><\/p>\n<div>\n<p>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;spu_removeInvalidData&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;P&#8217;)&#160; <br \/>DROP&#160;PROCEDURE&#160;spu_removeInvalidData; <br \/>GO <br \/>CREATE&#160;PROCEDURE&#160;spu_removeInvalidData&#160;&#160; <br \/>&#160;&#160;&#160;@numVotes&#160;dec(8,4) <br \/>AS <br \/>DECLARE&#160;@minTotalPoints&#160;dec(8,4),&#160;@maxTotalPoints&#160;dec(8,4); <br \/>IF&#160;EXISTS&#160;(SELECT&#160;TOP&#160;1&#160;*&#160;FROM&#160;possiblePoints) <br \/>BEGIN&#160; <br \/>&#160;&#160;&#160;SELECT&#160;@minTotalPoints&#160;=&#160;MIN(totalPoints),&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;@maxTotalPoints&#160;=&#160;MAX(totalPoints)&#160; <br \/>&#160;&#160;&#160;FROM&#160;possiblePoints; <br \/>END <br \/>ELSE <br \/>BEGIN <br \/>&#160;&#160;&#160;PRINT&#160;&#8217;There&#160;is&#160;no&#160;data&#160;in&#160;possiblePoints&#160;table.&#8217; <br \/>&#160;&#160;&#160;RETURN <br \/>END&#160;&#160;&#160; <\/p>\n<p>&#8211;&#160;Remove&#160;invalid&#160;data&#160;from&#160;table&#160;OneS <br \/>DELETE&#160;OneS&#160; <br \/>&#160;&#160;&#160;WHERE&#160;sumPoints&#160;+&#160;(@numVotes&#160;-&#160;votes)*5&#160;&lt;&#160;@minTotalPoints <br \/>&#160;&#160;&#160;OR <br \/>&#160;&#160;&#160;sumPoints&#160;+&#160;(@numVotes&#160;-&#160;votes)*1&#160;&gt;&#160;@maxTotalPoints; <br \/>SELECT&#160;*&#160;FROM&#160;Ones; <\/p>\n<p>&#8211;&#160;Remove&#160;invalid&#160;data&#160;from&#160;table&#160;TwoS <br \/>DELETE&#160;TwoS&#160; <br \/>&#160;&#160;&#160;WHERE&#160;sumPoints&#160;+&#160;(@numVotes&#160;-&#160;votes)*5&#160;&lt;&#160;@minTotalPoints <br \/>&#160;&#160;&#160;OR <br \/>&#160;&#160;&#160;sumPoints&#160;+&#160;(@numVotes&#160;-&#160;votes)*1&#160;&gt;&#160;@maxTotalPoints; <br \/>SELECT&#160;*&#160;FROM&#160;TwoS; <\/p>\n<p>&#8211;&#160;Remove&#160;invalid&#160;data&#160;from&#160;table&#160;ThreeS <br \/>DELETE&#160;ThreeS&#160; <br \/>&#160;&#160;&#160;WHERE&#160;sumPoints&#160;+&#160;(@numVotes&#160;-&#160;votes)*5&#160;&lt;&#160;@minTotalPoints <br \/>&#160;&#160;&#160;OR <br \/>&#160;&#160;&#160;sumPoints&#160;+&#160;(@numVotes&#160;-&#160;votes)*1&#160;&gt;&#160;@maxTotalPoints; <br \/>SELECT&#160;*&#160;FROM&#160;ThreeS; <\/p>\n<p>&#8211;&#160;Remove&#160;invalid&#160;data&#160;from&#160;table&#160;FourS <br \/>DELETE&#160;FourS&#160; <br \/>&#160;&#160;&#160;WHERE&#160;sumPoints&#160;+&#160;(@numVotes&#160;-&#160;votes)*5&#160;&lt;&#160;@minTotalPoints <br \/>&#160;&#160;&#160;OR <br \/>&#160;&#160;&#160;sumPoints&#160;+&#160;(@numVotes&#160;-&#160;votes)*1&#160;&gt;&#160;@maxTotalPoints; <br \/>SELECT&#160;*&#160;FROM&#160;FourS; <\/p>\n<p>&#8211;&#160;Remove&#160;invalid&#160;data&#160;from&#160;table&#160;FiveS <br \/>DELETE&#160;FiveS&#160; <br \/>&#160;&#160;&#160;WHERE&#160;sumPoints&#160;+&#160;(@numVotes&#160;-&#160;votes)*5&#160;&lt;&#160;@minTotalPoints <br \/>&#160;&#160;&#160;OR <br \/>&#160;&#160;&#160;sumPoints&#160;+&#160;(@numVotes&#160;-&#160;votes)*1&#160;&gt;&#160;@maxTotalPoints; <br \/>SELECT&#160;*&#160;FROM&#160;FiveS; <br \/>GO <br \/>EXEC&#160;spu_removeInvalidData&#160;@numVotes&#160;=&#160;15<\/p>\n<p>Results:<\/p>\n<p>point&#160;&#160;&#160;&#160;&#160;&#160; votes&#160;&#160;&#160;&#160;&#160;&#160; sumPoints<br \/>&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;-<br \/>1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0.0000<br \/>1 &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1.0000<br \/>.&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .<br \/>1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 5&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 5.0000<\/p>\n<p>point&#160;&#160;&#160;&#160;&#160;&#160; votes&#160;&#160;&#160;&#160;&#160;&#160; sumPoints<br \/>&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;-<br \/>2&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0.0000<br \/>2&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 2.0000<br \/>.&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .<br \/>2&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 6&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 12.0000<br \/>2&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 7&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 14.0000<\/p>\n<p>point&#160;&#160;&#160;&#160;&#160;&#160; votes&#160;&#160;&#160;&#160;&#160;&#160; sumPoints<br \/>&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;-<br \/>3&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0.0000<br \/>3&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 3.0000<br \/>.&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; . &#160;.<br \/>3&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 10&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 30.0000<br \/>3&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 11&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 33.0000<\/p>\n<p>point&#160;&#160;&#160;&#160;&#160;&#160; votes&#160;&#160;&#160;&#160;&#160;&#160; sumPoints<br \/>&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;-<br \/>4&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4.0000<br \/>4&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 2&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 8.0000<br \/>.&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .<br \/>4&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 14&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 56.0000<br \/>4&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 60.0000<\/p>\n<p>point&#160;&#160;&#160;&#160;&#160;&#160; votes&#160;&#160;&#160;&#160;&#160;&#160; sumPoints<br \/>&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;-<br \/>5&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0.0000<br \/>5&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 5.0000<br \/>.&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .&#160; .<br \/>5&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 12&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 60.0000<br \/>5&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 13&#160;&#160;&#160;&#160;&#160;&#160; &#160;&#160;&#160;65.0000<\/p>\n<\/div>\n<h4>Solution: How the Readers Voted <\/h4>\n<p>In order to find out how the readers voted, we now:<\/p>\n<ol>\n<li>Join tables OneS, TwoS, ThreeS, FourS and FiveS, using the Cross (or Cartesian) join.  <\/li>\n<li>Apply the following predicates to each intermediate join:\n<ul>\n<li>The summary of the votes in the join cannot exceed the given number of votes;  <\/li>\n<li>The summary of the points in the join cannot be greater than the maximum total points.<\/li>\n<\/ul>\n<\/li>\n<li>Include into the result (the outmost join) only the combinations, where the summary of the votes equals to the given number of votes. <\/li>\n<\/ol>\n<p>Running the script shown in Listing5 will show you how the readers voted in our example:<\/p>\n<p>Listing5. Get Final Result<\/p>\n<div>\n<p>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;spu_getFinalResult&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;P&#8217;)&#160; <br \/>DROP&#160;PROCEDURE&#160;spu_getFinalResult; <br \/>GO <br \/>CREATE&#160;PROCEDURE&#160;spu_getFinalResult&#160;&#160; <br \/>&#160;&#160;&#160;@numVotes&#160;INT <br \/>AS <br \/>DECLARE&#160;@minTotalPoints&#160;float,&#160;@maxTotalPoints&#160;float; <br \/>IF&#160;EXISTS&#160;(SELECT&#160;TOP&#160;1&#160;*&#160;FROM&#160;possiblePoints) <br \/>BEGIN&#160; <br \/>&#160;&#160;&#160;SELECT&#160;@minTotalPoints&#160;=&#160;MIN(totalPoints),&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;@maxTotalPoints&#160;=&#160;MAX(totalPoints)&#160; <br \/>&#160;&#160;&#160;FROM&#160;possiblePoints; <br \/>END <br \/>ELSE <br \/>BEGIN <br \/>&#160;&#160;&#160;PRINT&#160;&#8217;There&#160;is&#160;no&#160;data&#160;in&#160;possiblePoints&#160;table.&#8217; <br \/>&#160;&#160;&#160;RETURN <br \/>END&#160;&#160;&#160; <br \/>IF&#160;EXISTS(SELECT&#160;*&#160;FROM&#160;sysobjects&#160; <br \/>&#160;&#160;&#160;WHERE&#160;ID&#160;=&#160;(OBJECT_ID(&#8216;result&#8217;))&#160;AND&#160;xtype&#160;=&#160;&#8217;U&#8217;)&#160; <br \/>DROP&#160;TABLE&#160;result; <\/p>\n<p>SELECT&#160;t1234.Ones,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;t1234.Twos,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;t1234.Threes,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;t1234.Fours,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;t5.votes&#160;Fives, <br \/>&#160;&#160;&#160;&#160;&#160;&#160;(t1234.votes_1234&#160;+&#160;t5.votes)&#160;AS&#160;votes_12345,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;(t1234.sumPoints_1234&#160;+&#160;t5.sumPoints)&#160;AS&#160;sumPoints_12345 <br \/>&#160;&#160;INTO&#160;result <br \/>&#160;&#160;FROM&#160;&#160;&#160; <br \/>&#160;(SELECT&#160;t123.Ones,&#160;t123.Twos,&#160;t123.Threes,&#160;t4.votes&#160;AS&#160;Fours,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;(t123.votes_123&#160;+&#160;t4.votes)&#160;AS&#160;votes_1234,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;(t123.sumPoints_123&#160;+&#160;t4.sumPoints)&#160;AS&#160;sumPoints_1234&#160;&#160; <br \/>&#160;&#160;&#160;&#160;FROM <br \/>&#160;&#160;&#160;(SELECT&#160;t12.Ones,&#160;t12.Twos,&#160;t3.votes&#160;AS&#160;Threes,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;(t12.votes_12&#160;+&#160;t3.votes)&#160;AS&#160;votes_123,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;(t12.sumPoints_12&#160;+&#160;t3.sumPoints)&#160;AS&#160;sumPoints_123 <br \/>&#160;&#160;&#160;&#160;&#160;&#160;FROM <br \/>&#160;&#160;&#160;&#160;&#160;(SELECT&#160;t1.votes&#160;Ones,&#160;t2.votes&#160;Twos,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;(t1.votes&#160;+&#160;t2.votes)&#160;AS&#160;votes_12,&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;(t1.sumPoints&#160;+&#160;t2.sumPoints)&#160;AS&#160;sumPoints_12 <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;FROM&#160;OneS&#160;t1&#160;CROSS&#160;JOIN&#160;TwoS&#160;t2 <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;WHERE&#160;t1.votes&#160;+&#160;t2.votes&#160;&lt;=&#160;@numVotes&#160; <br \/>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;AND&#160;(t1.sumPoints&#160;+&#160;t2.sumPoints)&#160;&lt;=&#160;ROUND(@maxTotalPoints,0))&#160;t12 <br \/>&#160;&#160;&#160;&#160;&#160;CROSS&#160;JOIN&#160;ThreeS&#160;t3 <br \/>&#160;&#160;&#160;&#160;&#160;WHERE&#160;t12.votes_12&#160;+&#160;t3.votes&#160;&lt;=&#160;@numVotes&#160; <br \/>&#160;&#160;&#160;&#160;&#160;AND&#160;t12.sumPoints_12&#160;+&#160;t3.sumPoints&#160;&lt;=&#160;ROUND(@maxTotalPoints,0))&#160;t123 <br \/>&#160;&#160;&#160;CROSS&#160;JOIN&#160;FourS&#160;t4 <br \/>&#160;&#160;&#160;WHERE&#160;t123.votes_123&#160;+&#160;t4.votes&#160;&lt;=&#160;@numVotes&#160; <br \/>&#160;&#160;&#160;AND&#160;t123.sumPoints_123&#160;+&#160;t4.sumPoints&#160;&lt;=&#160;ROUND(@maxTotalPoints,0))&#160;t1234 <br \/>&#160;CROSS&#160;JOIN&#160;FiveS&#160;t5 <br \/>&#160;WHERE&#160;t1234.votes_1234&#160;+&#160;t5.votes&#160;=&#160;@numVotes&#160; <br \/>&#160;AND&#160;t1234.sumPoints_1234&#160;+&#160;t5.sumPoints&#160;BETWEEN&#160;ROUND(@minTotalPoints,0)&#160;<br \/>AND&#160;ROUND(@maxTotalPoints,0); <br \/>SELECT&#160;*&#160;FROM&#160;result; <\/p>\n<p>GO <br \/>EXEC&#160;spu_getFinalResult&#160;@numVotes&#160;=&#160;15;<\/p>\n<p>Results:<\/p>\n<p>Ones&#160;&#160; &#160;Twos&#160;&#160;&#160; Threes&#160; Fours&#160; Fives&#160; totalVotes&#160; totalPoints<br \/>&#8212;&#8212;- &#8212;&#8212;- &#8212;&#8212;- &#8212;&#8212; &#8212;&#8212; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8211;<br \/>0&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 60.0000<br \/>0&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160; 14&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 59.0000<br \/>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br \/>3&#160;&#160;&#160;&#160;&#160;&#160; 3&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160; 8&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 53.0000<br \/>4&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 3&#160;&#160;&#160;&#160;&#160; 8&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 56.0000<br \/>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br \/>2&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160; 12&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 65.0000<br \/>2&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160; 13&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 67.0000<\/p>\n<p>(871 row(s) affected)<\/p>\n<\/div>\n<p>The result of the calculation is in the table result. Each row of that table represents one possible voting combination. Columns One to Five store the number of readers that rated the article 1 to 5, out of 5, respectively. <\/p>\n<p>The number of rows in the table, i.e. the number of possible voting combinations is equal to 871 and this is probably the most frustrating part of the result. Indeed, only one combination from 871 possible ones is the correct answer, but there is no way to say which one. Therefore, for the given input (15 votes and the average rating 4, rounded to a whole number) it is impossible to uncover how readers voted. <\/p>\n<p>Does this mean, however, that the framework that we built, and all of the results, is useless? Well, no, because you still can get some interesting statistics from the result.<\/p>\n<p>For example, try the following query:<\/p>\n<div>\n<p>SELECT&#160;COUNT(OneS)&#160;FrequencyPerOnes,&#160;Ones&#160; <br \/>&#160;&#160;&#160;FROM&#160;result&#160; <br \/>&#160;&#160;&#160;GROUP&#160;BY&#160;Ones&#160; <br \/>&#160;&#160;&#160;ORDER&#160;BY&#160;Ones&#160;<\/p>\n<p>Results:<\/p>\n<p>FrequencyPerOnes Ones<br \/>&#8212;&#8212;&#8212;&#8212;&#8212;- &#8212;&#8212;&#8212;&#8211;<br \/>377&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0<br \/>254&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1<br \/>146&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 2<br \/>67&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 3<br \/>23&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 4<br \/>4&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 5<\/p>\n<\/div>\n<p>From this we can deduce that:<\/p>\n<ol>\n<li>There are no voting combinations where the number of ones exceeds 5;  <\/li>\n<li>There are 377 combinations, where no readers gave the article 1 point;  <\/li>\n<li>About 90% of all voting combinations are combinations where 2 or less readers rated the article 1 out of 5; <\/li>\n<\/ol>\n<p>Our technique can provide much more accurate results when the average rating has a higher precision (in most electronic journals and websites, the precision of the rating is one or two digits after the decimal point).<\/p>\n<p>For 15 votes and an average rating of 4.1 (rounded to one digit after the decimal point), you will get only 86 voting combinations (compare this to 871 combinations for the average rating 4):<\/p>\n<div>\n<p>EXEC&#160;spu_createVoteTables&#160;@numVotes&#160;=&#160;15; <br \/>GO <br \/>EXEC&#160;spu_findPossibleTotals&#160;@rating&#160;=&#160;4.1,&#160;@precision&#160;=&#160;1,&#160;@numVotes&#160;=&#160;15;&#160; <br \/>GO <br \/>EXEC&#160;spu_removeInvalidData&#160;@numVotes&#160;=&#160;15; <br \/>GO <br \/>EXEC&#160;spu_getFinalResult&#160;@numVotes&#160;=&#160;15;<\/p>\n<p>Results:<\/p>\n<p>Ones&#160; Twos&#160; Threes&#160; Fours&#160; Fives&#160; totalVotes&#160; totalPoints<br \/>&#8212;&#8211; &#8212;&#8211; &#8212;&#8212;- &#8212;&#8212; &#8212;&#8212; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;<br \/>0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 14&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160; &#160;15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 61.0000<br \/>0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 13&#160;&#160;&#160;&#160; 2&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<br \/>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . <br \/>2&#160;&#160;&#160;&#160; 2&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160; 11&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 61.0000<br \/>3&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160; 11&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<br \/>3&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 1 &#160;&#160;&#160;&#160;&#160;&#160;0&#160;&#160;&#160;&#160;&#160; 11&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 61.0000<\/p>\n<p>(86 row(s) affected)<\/p>\n<\/div>\n<p>For 15 votes with the average rating 4.13 (rounded to two digits after the decimal point), you will get only 39 voting combinations<\/p>\n<div>\n<p>EXEC&#160;spu_createVoteTables&#160;@numVotes&#160;=&#160;15; <br \/>GO <br \/>EXEC&#160;spu_findPossibleTotals&#160;@rating&#160;=&#160;4.13,&#160;@precision&#160;=&#160;2,&#160;@numVotes&#160;=&#160;15;&#160; <br \/>GO <br \/>EXEC&#160;spu_removeInvalidData&#160;@numVotes&#160;=&#160;15; <br \/>GO <br \/>EXEC&#160;spu_getFinalResult&#160;@numVotes&#160;=&#160;15;<\/p>\n<p>Results:<\/p>\n<p>Ones&#160; Twos&#160; Threes&#160; Fours&#160; Fives&#160; totalVotes&#160; totalPoints<br \/>&#8212;&#8211; &#8212;&#8211; &#8212;&#8212;- &#8212;&#8212; &#8212;&#8212; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;<br \/>0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 13&#160;&#160;&#160;&#160; 2&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<br \/>0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160; 11&#160;&#160;&#160;&#160; 3&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<br \/>. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br \/>2&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160; 11&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<br \/>3&#160;&#160; &#160;&#160;0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160; 11&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<br \/>1&#160;&#160;&#160;&#160; 3&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160; 11&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<\/p>\n<p>(39 row(s) affected)<\/p>\n<\/div>\n<p>This is actually as good as it gets. Use of a more precise average rating (with three or more digits after decimal point) will not reduce the number of possible voting combinations. <\/p>\n<p>However, if you have some additional information about the voting, you can narrow down your search still further.For example, the article has been rated by 15 readers and has a rating of 4.13 with the precision of 2. If, somehow, you know that at least one reader gave the article 1 point, you will get 18 possible combinations instead of 39:<\/p>\n<div>\n<p>SELECT&#160;*&#160;FROM&#160;result&#160;WHERE&#160;Ones&#160;&gt;&#160;0;<\/p>\n<p>Results:<\/p>\n<p>Ones&#160; Twos&#160; Threes&#160; Fours&#160; Fives&#160; totalVotes&#160; totalPoints<br \/>&#8212;&#8211; &#8212;&#8211; &#8212;&#8212;- &#8212;&#8212; &#8212;&#8212; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;<br \/>1&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 9&#160;&#160;&#160;&#160;&#160; 5&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<br \/>1&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160; 7&#160;&#160;&#160;&#160;&#160; 6&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<br \/>. . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br \/>3&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160; &#160;&#160;&#160;1 &#160;&#160;&#160;&#160;&#160;11&#160;&#160;&#160;&#160; 15&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;62.0000<br \/>1&#160;&#160;&#160;&#160; 3&#160;&#160;&#160;&#160; 0&#160;&#160; &#160;&#160;&#160;&#160;0&#160; &#160;&#160;&#160;&#160;11 &#160;&#160;&#160;&#160;15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<\/p>\n<p>(18 row(s) affected)<\/p>\n<\/div>\n<p>Furthermore, if you know that another reader gave the article 2 points, you will get only 9 possible combinations:<\/p>\n<div>\n<p>SELECT&#160;*&#160;FROM&#160;result&#160;WHERE&#160;Ones&#160;&gt;&#160;0&#160;AND&#160;Twos&#160;&gt;&#160;0; &#160;<\/p>\n<p>Results:<\/p>\n<p>Ones&#160; Twos&#160; Threes&#160; Fours&#160; Fives&#160; totalVotes&#160; totalPoints<br \/>&#8212;&#8211; &#8212;&#8211; &#8212;&#8212;- &#8212;&#8212; &#8212;&#8212; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;<br \/>1&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 6&#160;&#160;&#160;&#160;&#160; 7&#160;&#160;&#160;&#160;&#160; 15 &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;62.0000<br \/>1&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160; 4&#160;&#160;&#160;&#160;&#160; 8&#160;&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<br \/>. . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br \/>2&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160; 1&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160; 11&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<br \/>1&#160;&#160;&#160;&#160; 3&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160; 11&#160;&#160;&#160;&#160; 15&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 62.0000<\/p>\n<p>(9 row(s) affected)<\/p>\n<\/div>\n<p>You may be thinking that rating decomposition always results in multiple voting combinations, but this is not always the case. For some inputs, you can get just a few or even one voting combination.<\/p>\n<p>The following example for 8 votes and a rating of 4.75 will retrieve only two possible voting combinations:<\/p>\n<div>\n<p>EXEC&#160;spu_createVoteTables&#160;@numVotes&#160;=&#160;8; <br \/>GO <br \/>EXEC&#160;spu_findPossibleTotals&#160;@rating&#160;=&#160;4.75,&#160;@precision&#160;=&#160;2,&#160;@numVotes&#160;=&#160;8&#160; <br \/>GO <br \/>EXEC&#160;spu_removeInvalidData&#160;@numVotes&#160;=&#160;8; <br \/>GO <br \/>EXEC&#160;spu_getFinalResult&#160;@numVotes&#160;=&#160;8;<\/p>\n<p>Results:<\/p>\n<p>Ones&#160; Twos&#160; Threes&#160; Fours&#160; Fives&#160; totalVotes&#160; totalPoints<br \/>&#8212;&#8211; &#8212;&#8211; &#8212;&#8212;- &#8212;&#8212; &#8212;&#8212; &#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;<br \/>0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160;&#160;&#160; 2&#160;&#160;&#160;&#160;&#160; 6&#160;&#160;&#160;&#160;&#160; 8&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;38.0000<br \/>0&#160;&#160;&#160;&#160; 0&#160;&#160;&#160;&#160; 1 &#160;&#160;&#160;&#160;&#160;&#160;0&#160;&#160;&#160;&#160;&#160; 7&#160;&#160;&#160;&#160;&#160; 8&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 38.0000<\/p>\n<\/div>\n<p>As a rule of thumb, the accuracy of decomposition depends on the number of votes and on the precision that was used to calculate the rating. The more votes and the fewer digits after the decimal points in the rating, the more entropy (more possible voting combinations) will be in the result.<\/p>\n<h4>Performance Considerations <\/h4>\n<p>Our solution was based on cross or Cartesian join, which in turn generates the Cartesian product -a huge number of the rows in the result. Since the solution uses cross-joins four times, it will definitely experience performance problems for large number of votes. <\/p>\n<p>Indeed, when I ran the procedures on my single CPU desktop, for 87 votes and an average rating of 3.43, I got the result in 3 min. and 43 sec. For votes = 127, rating = 2.77, the execution time was 16 min. and 38 sec. <\/p>\n<p>You can get better execution time if you just generate the result, but do not insert it into the table result (as in the Listing5). In that case, the procedure <b>spu_getFinalResult<\/b> will run almost two times faster. Placing indexes on tables <b>OneS<\/b>, <b>TwoS<\/b>, <b>ThreeS<\/b>, <b>FourS<\/b> and <b>FiveS<\/b> may also improve the performance.<\/p>\n<p>Basically, you should be careful when using this solution for hundreds or thousands of votes as the algorithm we use here may become inefficient. Some good news, though, is that in real life, the number of votes for the articles (products, books) rarely goes over 100. <\/p>\n<p><b><\/b><\/p>\n<h3>Conclusions <\/h3>\n<p>How should you treat this article? Was it a fun puzzle? Was it something specific that can be applied only to very limited situations? <\/p>\n<p>Yes, ultimately, you should consider this article as a puzzle, although the algorithm provided can be very useful for some specific cases.<\/p>\n<p>For example, say you know the value of an aggregate function (<b>SUM<\/b> or <b>COUNT<\/b> or <b>AVG<\/b> and so on) and a few (from many) values that participated in the aggregate creation. If you do not have an access to a database (data) that produced the aggregate, how would you find the values that assembled the aggregate? This is the sort of situation where this algorithm can help.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When reading rating information, how do you you knew how many points each separate voter gave if you only know the average rating and the number of votes? Well, you might be surprised to learn that you can figure it out using SQL<br \/>\n&hellip;<\/p>\n","protected":false},"author":221828,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143531],"tags":[4150,4816,4252],"coauthors":[],"class_list":["post-301","post","type-post","status-publish","format-standard","hentry","category-t-sql-programming-sql-server","tag-sql","tag-sql-server-tsql","tag-t-sql-programming"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/301","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221828"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=301"}],"version-history":[{"count":2,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/301\/revisions"}],"predecessor-version":[{"id":39859,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/301\/revisions\/39859"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=301"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=301"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=301"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=301"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}