选择另一个表中另一列命名的列

时间:2016-09-09 22:12:19

标签: sql-server postgresql join

我正在实施一个支持角色扮演游戏的数据库。有两个相关的表: character weapon (加上表示标准多对多关系的第三个表;加上武器的每个特定实例的级别)。角色有多个属性(力量,敏捷,魔法等),每个武器都有一个基础伤害,一个等级(在多对多关联中定义),并从角色的相关属性中获得奖励,持有所述武器(俱乐部的力量,远程武器的敏捷性等)。武器的有效性必须来自三个表格。问题是角色表的哪一列适用取决于所使用的特定武器。

我当前的范例是执行两个选择查询,一个用于从武器表中检索关联属性(varchar)的名称,然后一个 - 用先前返回的值替换 - 用于来自挥动的该属性的值字符。我想用纯sql解决方案替换它。

我在网上搜索过,发现了另外两个问题: Pivot on Multiple Columns using TablefuncPostgreSQL 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引擎)。

1 个答案:

答案 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