问题
I'm doing some request to help a game dev to balance his game, i'm trying to see how many player use what rune, and at what average level
here is my code :
declare @runeID varchar(100)
set @runeID=22001
select counT(i.characterid) as 'user level 1 to 10', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>0 and level<11 and attached>0
select counT(i.characterid) as 'user level 11 to 20', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>10 and level<21 and attached>0
select counT(i.characterid) as 'user level 21 to 30', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>20 and level<31 and attached>0
select counT(i.characterid) as 'user level 31 to 40', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>30 and level<41 and attached>0
select counT(i.characterid) as 'user level 41 to 50', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>40 and level<51 and attached>0
select counT(i.characterid) as 'user level 51 to 60', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>50 and level<61 and attached>0
select counT(i.characterid) as 'user level 61 to 70', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>60 and level<71 and attached>0
select counT(i.characterid) as 'user level 71 to 80', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>70 and level<81 and attached>0
select counT(i.characterid) as 'user level 81 to 90', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>80 and level<91 and attached>0
select counT(i.characterid) as 'user level 91 to 100', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>90 and level<101 and attached>0
select counT(i.characterid) as 'user level 101 to 110', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>100 and level<111 and attached>0
select counT(i.characterid) as 'user level 111 to 120', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>110 and level<121 and attached>0
select counT(i.characterid) as 'user level 121 to 130', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>120 and level<131 and attached>0
select counT(i.characterid) as 'user level 131+', avg(i.maxUpgrade) as 'average level' from items i inner join characters c on i.characterId=c.characterId
where itemId=@runeID and level>130 and attached>0
This code give me usage of the rune i select in my variable each 10 levels. I got this code from someone else helping me (Gordon Linoff):
select floor(level / 10) * 10 as range_start,
avg(i.maxUpgrade) as avg_level,
count(i.characterId) as number_of_user
from items i inner join
characters c
on i.characterId = c.characterId
where attached > 0
group by floor(level / 10) * 10
order by range_start ASC
this code shorten what i done and i was wondering if I could use a while
to create new column for each runeId I put before in a list (12001,12002,12002,etc...), so I could get something like that :
22001_use 22001_avg_lvl 22002_use 22002_avg_lvl
level_1-9
level_10-19
level_20-29
so in in first column an indicator for level range, 2nd and 3rd column the number of player playing that rune each 10 levels according to first column, and the average level peoples use that rune, each rune in the list create 2 new column for use and avg level
So this way if me or the dev need the most recent stats, he just have to run the query, copy the result and past it in a google sheet, also with the list if he decides to add more runes it would be easier to update the code
回答1:
I think you want conditional aggregation:
select
floor(level / 10) * 10 as range_start,
sum(case when i.itemid = 22001 then 1 else 0 end) as use_22001
avg(case when i.itemid = 22001 then i.maxUpgrade end) as avg_lvl_22001,
sum(case when i.itemid = 22002 then 1 else 0 end) as use_22002
avg(case when i.itemid = 22002 then i.maxUpgrade end) as avg_lvl_22002
from items i
inner join characters c on i.characterId = c.characterId
where attached > 0 and i.item_id in (22001, 22002)
group by floor(level / 10) * 10
sort by range_start ASC
回答2:
Here's an attempt to refactor the code. Since 'level' is an integer (from the characters table) there's no need to take the FLOOR
. Eliminating that and removing the calculation to a CROSS APPLY'ed virtual table and column 'lvl.lvl'. Then for some reason there's a 'sort by' in the code when it should be ORDER BY
. Also, there were some missing commas. Something like this.
select lvl.lvl as range_start,
sum(case when i.itemid = 22001 then 1 else 0 end) as use_22001, avg(case when i.itemid = 22001 then i.maxUpgrade end) as avg_lvl_22001,
sum(case when i.itemid = 22002 then 1 else 0 end) as use_22002, avg(case when i.itemid = 22002 then i.maxUpgrade end) as avg_lvl_22002,
avg(i.maxUpgrade) as tot_avg_level,
count(i.characterId) as tot_num_users
from items i
join characters c on i.characterId = c.characterId
cross apply (select (c.[level]/10)*10 lvl) lvl
where attached > 0
group by lvl.lvl
order by lvl.lvl;
To build the SQL dynamically so that it creates the 2 columns for each rune (represented by an itemid) in the items table, something like this
declare
@select nvarchar(max)=N'select lvl.lvl as range_start, ',
@str1 nvarchar(max)=N' sum(case when i.itemid = ',
@str2 nvarchar(max)=N' then 1 else 0 end) as use_',
@str3 nvarchar(max)=N', avg(case when i.itemid = ',
@str4 nvarchar(max)=N' then i.maxUpgrade end) as avg_lvl_',
@str5 nvarchar(max)=N',',
@from nvarchar(max)=N' avg(i.maxUpgrade) as tot_avg_level,
count(i.characterId) as tot_num_users
from items i
join characters c on i.characterId = c.characterId
cross apply (select (c.[level]/10)*10 lvl) lvl
where attached > 0
group by lvl.lvl
order by lvl.lvl;',
@sql nvarchar(max);
select @sql=concat(@select,
string_agg(concat(@str1, cast(itemid as char(5)),
@str2, cast(itemid as char(5)),
@str3, cast(itemid as char(5)),
@str4, cast(itemid as char(5)),
@str5),
@from)
from items
where itemid>22000
and itemid<24000;
exec sp_executesql @sql;
来源:https://stackoverflow.com/questions/64448191/regrouping-all-results-in-a-select-with-a-while