我正在实施一个支持角色扮演游戏的数据库。有两个相关的表: character 和 weapon (加上表示标准多对多关系的第三个表;加上武器的每个特定实例的级别)。角色有多个属性(力量,敏捷,魔法等),每个武器都有一个基础伤害,一个等级(在多对多关联中定义),并从角色的相关属性中获得奖励,持有所述武器(俱乐部的力量,远程武器的敏捷性等)。武器的有效性必须来自三个表格。问题是角色表的哪一列适用取决于所使用的特定武器。
我当前的范例是执行两个选择查询,一个用于从武器表中检索关联属性(varchar)的名称,然后一个 - 用先前返回的值替换 - 用于来自挥动的该属性的值字符。我想用纯sql解决方案替换它。
我在网上搜索过,发现了另外两个问题: Pivot on Multiple Columns using Tablefunc和PostgreSQL Crosstab Query,但我找不到。我还找到了postgres内部数据类型oid [https://www.postgresql.org/docs/9.1/static/datatype-oid.html],并且能够找到特定列的oid,但找不到使用该oid查询列值的语法。
表格方案:
create table character (
id int primary key,
agility int,
strength int,
magic int,
...);
create table weapon (
id int primary key,
damage int,
associated_attribute varchar(32), --this can be another type if it'd help
...);
create table weapon_character_m2m (
id int primary key,
weapon int, --foreign key to weapon.id
character int, --foreign key to character.id
level int);
在我看来,这应该可以通过这样的方式查询(理想情况下会导致玩家拥有的每种武器的有效伤害。):
select m2m.level as level,
weapon.associated_attribute as base_attr_name,
character.??? as base_attr,
weapon.damage as base_damage,
base_damage * base_attr * level as effective_attr -- this is the column I care about, others are for clarity via alias
from weapon_character_m2m as m2m
join weapon on weapon.id=m2m.weapon
join character on character.id=m2m.character;
where m2m.character=$d -- stored proc parameter or the like
我发现大多数在线资源最终建议重新设计数据库。这是一个选项,但我真的不希望武器可能关联的每个属性都有一个不同的表(实际上有近20个可能与武器类相关的属性)。
我听说在MSSQL中通过外键进入内部系统表是可能的,但我没有使用MSSQL的经验,更不用说尝试类似的东西了(而且我不能在互联网上找不到工作样本。如果有人能提供一个有效的例子,我会考虑迁移到MSSQL(或任何其他sql引擎)。
答案 0 :(得分:1)
听起来你可以使用CASE语句。我知道它是在MS SQL中...不确定PostgreSQL。
类似的东西(在我的手机上只是估算代码):
Select other fields,
case weapon.associated_attribute
when 'agility' then character.agility
when 'strength' then character.strength
when ...
else 0 --unhandled associate_attribute
end as base_attr
from ...
这里需要注意的是,您希望您的角色属性与您的角色属性相同。
修改强>
我根据您的反馈努力建立了一个视图,并意识到视图将使用一个unpivot而不是上面的case语句,但您可以使用一个函数来使用上面的CASE结构来完成它。有很多种口味:-) MS SQL也有表值函数,可以用来为所有字符返回一个属性类型。这是我正在玩的代码。它包含视图和功能。您可以选择哪个更合适。
create table character (
id int primary key,
agility int,
strength int,
magic int,
--...
);
insert character values (1,10,15,20),(2,11,12,13);
create table attribute_type (
attribute_id int primary key,
attribute_name varchar(20)
);
insert attribute_type values (1,'Agility'),(2,'Strength'),(3,'Magic');
create table weapon (
id int primary key,
damage int,
--associated_attribute varchar(32), --this can be another type if it'd help
attribute_id int
--...
);
insert weapon values (1,20,1),(2,30,2);
create table weapon_character_m2m (
id int primary key,
weapon int, --foreign key to weapon.id
character int, --foreign key to character.id
level int);
insert weapon_character_m2m values (1,1,1,4),(2,2,2,5);
go
create view vw_character_attributes
as
select c.id, a.attribute_id, c.attribute_value
from (
select id, attribute_name, attribute_value
from (select id, agility, strength, magic from character) p --pivoted data
unpivot (attribute_value for attribute_name in (agility, strength, magic)) u --unpivoted data
) c
join attribute_type a on a.attribute_name = c.attribute_name
;
go
create function fn_get_character_attribute (@character_id int, @attribute_id int)
returns int
as
begin
declare @attr int;
select @attr =
case @attribute_id
when 1 then c.agility
when 2 then c.strength
when 3 then c.magic
--when ...
else 0 --unhandled associate_attribute
end
from character c
where c.id = @character_id;
return @attr;
end
go
select * from vw_character_attributes;
select m2m.level as level,
at.attribute_name as base_attr_name,
ca.attribute_value as base_attr,
dbo.fn_get_character_attribute(m2m.id, weapon.attribute_id ) function_value,
weapon.damage as base_damage,
weapon.damage * ca.attribute_value * level as effective_attr -- this is the column I care about, others are for clarity via alias
from weapon_character_m2m as m2m
join weapon on weapon.id=m2m.weapon
join vw_character_attributes ca on ca.id=m2m.character and ca.attribute_id = weapon.attribute_id
join attribute_type at on at.attribute_id = weapon.attribute_id
--where m2m.character=$d; -- stored proc parameter or the like