今天去笔试:遇到一个题目,题目大概是这样的:
有一个试题表:T_EXAM {ID,type(1,2,3/表示试题类型),difficulty(1,2/试题难度),distinguish(1,2/区分)}
现在我要从题库随机抽出20道题,type:类型1的6道,2的7道,3的7道;difficulty:难度1的8道,2的12道;distinguish:区分1的13道,2的7道:请问能用SQL查询出来吗?
如果能用SQL查询出来,SQL语句该怎么写?
有一个试题表:T_EXAM {ID,type(1,2,3/表示试题类型),difficulty(1,2/试题难度),distinguish(1,2/区分)}
现在我要从题库随机抽出20道题,type:类型1的6道,2的7道,3的7道;difficulty:难度1的8道,2的12道;distinguish:区分1的13道,2的7道:请问能用SQL查询出来吗?
如果能用SQL查询出来,SQL语句该怎么写?
select top 6 * from tb where type=1 and difficulty=1 and distinguish=1 oder by newid()
union all
select top 2 * from tb where type=2 and difficulty=1 and distinguish=1 oder by newid()
union all
select top 5 * from tb where type=2 and difficulty=2 and distinguish=1 oder by newid()
union all
select top 7 * from tb where type=2 and difficulty=2 and distinguish=2 oder by newid()
begin
drop table [T_EXAM]
end CREATE TABLE [dbo].[T_EXAM](
[ID] [int],
[type] [int] NULL,
[difficulty] [int] NULL,
[distinguish] [int] NULL
) ON [PRIMARY]
insert into [T_EXAM]([ID],[type],[difficulty],[distinguish])
select top 10000 row_number()over(order by newid()), abs(checksum(newid())%3)+1,abs(checksum(newid())%2)+1,abs(checksum(newid())%2)+1 from master.dbo.spt_values a
declare @match int
declare @count1 int
declare @count2 int
set @match=0
while (@match<>1)
begin if object_id('tempdb..#temp') is not null
drop table #temp select top 6 * into #temp from T_EXAM
where type=1
order by newid() insert into #temp
select top 7 * from T_EXAM
where type=2
order by newid() insert into #temp
select top 7 * from T_EXAM
where type=3
order by newid()
/*8*/
select @count1=count(*) from #temp
where [difficulty]=1
/*13*/
select @count2=count(*) from #temp
where [distinguish]=1print @count1
print @count2
if (@count1=8 and @count2=13)
begin
set @match=1
break
endend select * from #temp
然后判断是否满足diff=1的有8条。dis=1的有13条。如果有终止循环然后返回20条记录。不然重新随机去20条记录计算。
缺点是while循环要指定一个次数,不然如果没有满足的结果集。会变成死循环。
很傻的算伐。
id int ,
[type] int,
[difficurity] int,
[distinguish] int);
insert into #tb select top 1 * from T_EXAM order by NEWID()declare @count int=1while @count<20
beginbegin tran
insert into #tb select top 1 * From T_EXAM
order by NEWID()
if 6 <(select COUNT(*) from #tb where [TYPE]=1)
or 7 <(select COUNT(*) from #tb where [TYPE]=2)
or 7 <(select COUNT(*) from #tb where [TYPE]=3)
or 8 <(select COUNT(*) from #tb where [difficurity]=1)
or 12<(select COUNT(*) from #tb where [difficurity]=2)
or 13<(select COUNT(*) from #tb where [distinguish]=1)
or 7 <(select COUNT(*) from #tb where [distinguish]=2)
or exists( select * From #tb group by id having COUNT(*)>1)
rollback tran
else
commit tran
select @count=COUNT(*)From #tbend--查看结果
select [TYPE],count(*) From #tb
group by [TYPE]select [difficurity],count(*) From #tb
group by [difficurity]select [distinguish],COUNT(*) From #tb
group by [distinguish]select * From #tb order by [TYPE],[difficurity],[distinguish]drop table #tb
insert into #tb select top 1 *...
中的top 1 改成 top (n)
n=所有条件中的最小数,这里是6,这样可以快一些。
select top 6 * from tb where type=1 and difficulty=1 and distinguish=1 oder by newid()
union all
select top 2 * from tb where type=2 and difficulty=1 and distinguish……
select top 6 * from tb where type=1 and difficulty=1 and distinguish=1 oder by newid()
union all
select top 2 * from tb where type=2 and difficulty=1 and distinguish……
while 1 = 1
begin
insert into #ta
select top(20)* from T_EXAM order by newid()
if exists(select sum(1) from #ta
having
sum(case when type = 1 then 1 else 0 end) = 6 and
sum(case when type = 2 then 1 else 0 end) = 7 and
sum(case when type = 3 then 1 else 0 end) = 7 and
sum(case when difficulty = 1 then 1 else 0 end) = 8 and
sum(case when difficulty = 2 then 1 else 0 end) = 12 and
sum(case when distinguish = 1 then 1 else 0 end) = 13 and
sum(case when distinguish = 2 then 1 else 0 end) = 7)
break
truncate table #ta
end
select * from #ta
/*
ID type difficulty distinguish
----------- ----------- ----------- -----------
4297 2 2 1
9304 3 2 2
7774 1 2 1
3043 1 1 2
764 2 2 1
4727 3 2 2
9248 1 1 1
4137 3 1 2
3594 2 2 1
2234 3 2 1
7964 1 2 1
2883 1 2 1
3694 2 1 1
4097 3 2 2
2638 3 1 2
4635 3 2 2
1736 1 1 1
7866 2 2 1
1937 2 1 1
5590 2 1 1(20 行受影响)*/
create table #ta(ID int , type int,difficulty int, distinguish int)
while 1 = 1
begin
insert into #ta
select top(20)* from T_EXAM order by ……
id type
1,1
2,1
3,1
...
20,3
(1的6道,2的7道,3的7道)
临时表 #t2
id difficulty
1,1
2,1
...
20,2
(难度1的8道,2的12道)
临时表 #t3
id distinguish
1,1
2,1
...
20,2
(1的13道,2的7道)declare #Result
(
[ID] [int],
[type] [int] NULL,
[difficulty] [int] NULL,
[distinguish] [int] NULL
)declare @i int
set @i = 1
while (@i <= 20)
begin
insert into #Result (ID,type,difficulty,distinguish)
select top 1 ID,type,difficulty,distinguish
from T_EXAM t1
where type = (select type from #t1 where id=@i)
and difficulty = (select difficulty from #t2 where id=@i)
and distinguish= (select distinguish from #t3 where id=@i)
and not exists(select * from #Result r where r.id=t1.id)
order by newid() set @i = @i + 1
endselect * from #Result
简单的写应该是这样的,期待更牛逼的算法。SQL code
select top 6 * from tb where type=1 and difficulty=1 and distinguish=1 oder by newid()
union all
select top 2 * from tb where type=2 and difficulty=1 and distingu……
有点不明白为什么这样写,那样不是有很多种组合!
发现一个问题就是会全表扫描,如果数据量大的话不可取。好像有个函数是抽样的,可以抽样数据出来,这个可以试一下,效率应该有很大的提升。
应改为:
insert into #tb select top 1 * From T_EXAM
where id not in (select id from #tb)
order by NEWID()
// 完全参数处理
create table #t1(id int,type int)
create table #t2(id int,difficulty int)
create table #t3(id int,distinguish int)
declare @type1 int
set @type1 = 6
declare @type2 int
set @type2 = 7
declare @type3 int
set @type3 = 7declare @i int
set @i = 1
while (@i <= @type1 )
begin
insert into #t1(id ,int) values(@i, 1)
set @i = @i + 1
end
while (@i <= @type1 + @type2 )
begin
insert into #t1(id ,int) values(@i, 2)
set @i = @i + 1
end
while (@i <= @type1 + @type2 + @type3 )
begin
insert into #t1(id ,int) values(@i, 3)
set @i = @i + 1
end//#t2, #t3同样处理declare table #Result
(
[ID] [int],
[type] [int] NULL,
[difficulty] [int] NULL,
[distinguish] [int] NULL
)
set @i = 1
while (@i <= 20)
begin
insert into #Result (ID,type,difficulty,distinguish)
select top 1 ID,type,difficulty,distinguish
from T_EXAM t1
where type = (select type from #t1 where id=@i)
and difficulty = (select difficulty from #t2 where id=@i)
and distinguish= (select distinguish from #t3 where id=@i)
and not exists(select * from #Result r where r.id=t1.id)
order by newid() set @i = @i + 1
endselect * from #Result
select * from (select t.*,ROW_NUMBER() OVER(ORDER BY dbms_random.value(1,99999999)) as rNum from tb t where type=1 and difficulty=1 and distinguish=1) where rNum < 7
union all
select * from (select t.*,ROW_NUMBER() OVER(ORDER BY dbms_random.value(1,99999999)) as rNum from tb t where type=2 and difficulty=1 and distinguish=1) where rNum < 3
union all
select * from (select t.*,ROW_NUMBER() OVER(ORDER BY dbms_random.value(1,99999999)) as rNum from tb t where type=2 and difficulty=2 and distinguish=1) where rNum < 6
union all
select * from (select t.*,ROW_NUMBER() OVER(ORDER BY dbms_random.value(1,99999999)) as rNum from tb t where type=2 and difficulty=2 and distinguish=2) where rNum < 8
;
重复已经ROLLBACK了。
从现实考虑,题库有几万记录几乎顶天了,10楼的方案不怕题库大而是怕题库小,题库越小ROLLBACK的几率越大,性能是随机的。
应该添加rollback计数,如果题库不够大,有可能永远无法insert最后1条或几条(即使绝对可以抽出这样的20题,但前面的分配错了)。
6+2+xx+xx=20 提取20条记录对吧
咱可以这样么:
数据库表里 一般都有编号对吧
如果 有1001条记录
随即1001 里20个数 不重复的 然后 去数据表里查可以么 我个人看法 等待高人指点 谢谢
id int ,
[type] int,
[difficurity] int,
[distinguish] int);
declare @type1 int
declare @type2 int
declare @type3 int
declare @difficulty1 int
declare @difficulty2 int
declare @distinguish1 int
declare @distinguish2 intset @type1=1
set @type2=2
set @type3=3
set @difficulty1=1
set @difficulty2=2
set @distinguish1=1
set @distinguish2=2declare @i int
set @i = 1while (@i <= 20)
begin if (select COUNT(*) from #tb where [TYPE]=1) = 6
set @type1 = 0
if (select COUNT(*) from #tb where [TYPE]=2) = 7
set @type2 = 0
if (select COUNT(*) from #tb where [TYPE]=3) = 7
set @type3 = 0
if (select COUNT(*) from #tb where [difficurity]=1) = 8
set @difficulty1=0
if (select COUNT(*) from #tb where [difficurity]=2) = 12
set @difficulty2=0
if (select COUNT(*) from #tb where [distinguish]=1) = 13
set @distinguish1=0
if (select COUNT(*) from #tb where [distinguish]=2) = 7
set @distinguish2=0 insert into #t
select top 1 *
from T_EXAM te
where [TYPE] in (@type1,@type2,@type3)
and [difficurity] in (@difficulty1,@difficulty2)
and [distinguish] in (@distinguish1,@distinguish2)
and not exists(select * from #t t2 where te.[id]=t2.[id])
order by newid() if @@RowCount = 0 //已没有符合记录
break;
else
set @i = @i + 1
endselect * from #t
--我也写了一个,真正的随机
WHILE 1=1
BEGIN
--按每20条随机分组
SELECT GID=(ROW_NUMBER() OVER(ORDER BY NEWID())-1)/20,* INTO #t FROM T_EXAM --把符合要求的分组筛选出来
SELECT GID INTO #t2 FROM #t GROUP BY GID HAVING COUNT(*) = 20
AND SUM(CASE WHEN [type] = 1 THEN 1 ELSE 0 END) = 6
AND SUM(CASE WHEN [type] = 2 THEN 1 ELSE 0 END) = 7
AND SUM(CASE WHEN [type] = 3 THEN 1 ELSE 0 END) = 7
AND SUM(CASE WHEN [difficurity] = 1 THEN 1 ELSE 0 END) = 8
AND SUM(CASE WHEN [difficurity] = 2 THEN 1 ELSE 0 END) = 12
AND SUM(CASE WHEN [distinguish] = 1 THEN 1 ELSE 0 END) = 13
AND SUM(CASE WHEN [distinguish] = 2 THEN 1 ELSE 0 END) = 7
--如果找到了,就结束
IF @@ROWCOUNT > 0
BEGIN
SELECT b.ID,b.[type],b.[difficurity],b.[distinguish] FROM #t2 a JOIN #t b ON a.GID = b.GID ORDER BY a.GID
BREAK
END
--如果没找到,就再来,呵呵
DROP TABLE #t
DROP TABLE #t2
END