7

Assume a table structure:

Create Table Question
{
ID int pk,
Category varchar
Stem varchar,
AnswerA varchar,
...
AnswerD varchar,
Correct char,
isMandatory bit

}

For a given category, there are approximately 50 questions. There can be 1-10 mandatory questions.

I need to select all mandatory questions, and then enough other questions at random to make a question set of 20 questions.

Bridge
  • 29,818
  • 9
  • 60
  • 82
Chris Cudmore
  • 29,793
  • 12
  • 57
  • 94

3 Answers3

7

Ok how about this

select top 20 * from question
where category = @category
order by isMandatory desc, newid()

See accepted answer for reasoning behind newid() Random record from a database table (T-SQL)

Community
  • 1
  • 1
Chris Moutray
  • 18,029
  • 7
  • 45
  • 66
  • The accepted answer there doesn't mention `CRYPT_GEN_RANDOM`, introduced in 2008. Unlike `RAND` this is evaluated per row. – Martin Smith Jun 03 '12 at 19:17
  • This is untested SQL, are you saying that the guid would be the same for all records, and therefore the result wouldn't be random? – Chris Moutray Jun 03 '12 at 19:30
  • No. `NEWID()` works fine though I'm unsure as to the actual degree of randomness it offers. That is the issue with the more obvious choice of `RAND` (as mentioned in the answer you linked) and why people resorted to using `NEWID`. – Martin Smith Jun 03 '12 at 19:33
  • In that case I suspect the level of randomness is adequate for OP's needs, but worth pointing out... – Chris Moutray Jun 03 '12 at 19:54
  • I would have thought so too. I presume the OP just wants differing results between executions and a reasonable shuffling of the non mandatory questions but I've seen some people get quite agitated about this issue! [example in the comments here](http://stackoverflow.com/a/10016964/73226) – Martin Smith Jun 03 '12 at 19:57
  • Yeah I'm not too concerned about statistical randomization. I"m happy with randomish. This ended up working well. (although I had to modify it to reflect the actual gawdawful table I was given, but this solution got me on the right track.) – Chris Cudmore Jun 03 '12 at 20:56
6
;WITH T 
     AS (SELECT *, 
                ROW_NUMBER() 
                  OVER (PARTITION BY Category 
                            ORDER BY isMandatory DESC, CRYPT_GEN_RANDOM(4)) RN 
         FROM   Question) 
SELECT * 
FROM   T 
WHERE  RN < = 20 
Martin Smith
  • 438,706
  • 87
  • 741
  • 845
  • I was interested in the performance in the `NEWID()` vs `CRYPT_GEN_RANDOM(4)`, but ordering by `CRYPT_GEN_RANDOM(4)` doesn't randomise the order; what am I doing wrong? – Chris Moutray Jun 03 '12 at 20:13
  • declare @ data table (val int); insert into @ data values (1), (2), (3), (4), (5) – Chris Moutray Jun 03 '12 at 20:14
  • insert into @ data values (1), (2), (3), (4), (5) – Chris Moutray Jun 03 '12 at 20:14
  • select * from @ data order by NEWID() – Chris Moutray Jun 03 '12 at 20:14
  • @mouters - What is your `@@VERSION`? [I reported a bug with it here](http://connect.microsoft.com/SQLServer/feedback/details/654809/crypt-gen-random-erroneously-treated-as-constant) – Martin Smith Jun 03 '12 at 20:15
  • select * from @ data order by CRYPT_GEN_RANDOM(4) – Chris Moutray Jun 03 '12 at 20:15
  • Microsoft SQL Server 2008 R2 (SP1) - 10.50.2500.0 (X64) Jun 17 2011 00:54:03 Copyright (c) Microsoft Corporation Developer Edition (64-bit) on Windows NT 6.1 (Build 7601: Service Pack 1) – Chris Moutray Jun 03 '12 at 20:15
  • Sorry my example sql is completely messed up; but hopefully you get the idea – Chris Moutray Jun 03 '12 at 20:16
  • I'm on `Microsoft SQL Server 2008 (SP3) - 10.0.5500.0 (Intel X86) Sep 22 2011` and your query randomises fine for me. Shame they don't give any info about what specific service packs the fix is in in their response but the response and your SP are both June 2011 so maybe in the next one! – Martin Smith Jun 03 '12 at 20:20
  • 1
    @mouters - [Looks like it is fixed in 2008 R2 SP2 (Issue #820796)](http://support.microsoft.com/kb/2630455) – Martin Smith Jun 03 '12 at 20:31
  • 1
    Thanks, I'll look out for that; for what its worth an `outer apply` allows for a workaround `select * from @data outer apply ( select ordered = CRYPT_GEN_RANDOM(4) ) o order by o.ordered` to force row-by-row evaluation – Chris Moutray Jun 03 '12 at 20:40
0
   Declare @number_of_nonmandat INT

   Select @number_of_nonmandat=count(1)
   FROM Question
   where isMandatory =1

   SET @number_of_nonmandat=20-number_of_nonmandat;

    IF(@number_of_nonmandat>0)
    BEGIN
         Select *
         FROM Question
         where isMandatory =1
           UNION 
         SELECT TOP (@number_of_nonmandat)  *
         FROM Question
         where isMandatory<>1
         ORDER BY newID()
    END
    ELSE 
    BEGIN
        Select top 20 *
         FROM Question
         where isMandatory =1

    END
Ruzbeh Irani
  • 2,318
  • 18
  • 10