sql2005
这是我的简化示例: (实际上这里有40多张桌子,我只展示了2张)
我有一个名为tb_modules的表,有3列(id,description,tablename as varchar):
1, UserType, tb_usertype
2, Religion, tb_religion
(最后一列实际上是另一个表的名称)
我有另一个看起来像这样的表: tb_value(columns:id,tb_modules_ID,usertype_OR_religion_ID) 值:
1111, 1, 45
1112, 1, 55
1113, 2, 123
1114, 2, 234
所以,我的意思是45,55,123,234是usertype或宗教ID (45,55 usertype,123,234宗教ID`s)
不判断,我没有设计数据库
问题 如何进行选择,显示*来自tb_value,加上一列 那一列将来自tb_usertype的TITLE或来自tb_religion表的RELIGIONNAME
我想做一般事情。 最初想的可能是一个返回字符串的SQL函数,但我想我需要动态SQL,这在函数中是不行的。
任何人都有更好的主意吗?
答案 0 :(得分:2)
一开始我们有这个 - 这非常混乱。
为了清理一点,我添加了两个视图和一个同义词:
create view v_Value as
select
ID as ValueID
, tb_modules_ID as ModuleID
, usertype_OR_religion_ID as RemoteID
from tb_value ;
go
create view v_Religion as
select
ID
, ReligionName as Title
from tb_religion ;
go
create synonym v_UserType for tb_UserType ;
go
现在模型看起来像
现在编写查询更容易
;
with
q_mod as (
select
m.ID as ModuleID
, coalesce(x1.ID , x2.ID) as RemoteID
, coalesce(x1.Title , x2.Title) as Title
, m.Description as ModuleType
from tb_Modules as m
left join v_UserType as x1 on m.TableName = 'tb_UserType'
left join v_Religion as x2 on m.TableName = 'tb_Religion'
)
select
a.ModuleID
, v.ValueID
, a.RemoteID
, a.ModuleType
, a.Title
from q_mod as a
join v_Value as v on (v.ModuleID = a.ModuleID and v.RemoteID = a.RemoteID) ;
此查询中有一个明显的模式,因此如果必须添加另一个模块类型表,则可以将其创建为动态sql。添加其他表时,请使用ID
和Title
以避免使用视图。
修改强>
构建动态sql(或在应用程序级别查询)
修改第6行和第7行,x-index为tb_modules.id
coalesce(x1. , x2. , x3. ..)
向左连接添加行(第11行下方)
left join v_SomeName as x3 on m.TableName = 'tb_SomeName'
SomeName
为tb_modules.description
且x-index匹配tb_modules.id
编辑2
最简单的可能是将上述查询打包到视图中,然后每次架构动态更改crate并运行ALTER VIEW
。这样查询就不会从应用程序的角度发生变化。
答案 1 :(得分:2)
既然我们都同意这个设计是不稳定的,我会跳过任何评论。查询的模式是:
-- Query 1
select tb_value.*,tb_religion.religion_name as ANY_DESCRIPTION
from tb_value
JOIN tb_religion on tb_value.ANY_KIND_OF_ID = tb_religion.id
WHERE tb_value.module_id = 2
-- combine it with...
UNION ALL
-- ...Query 2
select tb_value.*,tb_religion.title as ANY_DESCRIPTION
from tb_value
JOIN tb_userType on tb_value.ANY_KIND_OF_ID = tb_userType.id
WHERE tb_value.module_id = 1
-- combine it with...
UNION ALL
-- ...Query 3
select lather, rinse, repeat for 40 tables!
您实际上可以定义一个硬编码所有40个案例的视图,然后将过滤器放到对所需特定模块的查询中。
答案 2 :(得分:2)
要动态执行此操作,您需要能够创建一个类似于此
的sql语句select tb_value.*, tb_usertype.title as Descr
from tb_value
inner join tb_usertype
on tb_value.extid = tb_usertype.id
where tb_value.tb_module_id = 1
union all
select tb_value.*, tb_religion.religionname as Descr
from tb_value
inner join tb_religion
on tb_value.extid = tb_religion.id
where tb_value.tb_module_id = 2
-- union 40 other tables
目前您不能这样做,因为数据库中没有任何信息告诉您使用tb_religion和tb_usertype等哪个列。您可以将其添加为tb_module中的新字段。
如果您在tb_module中使用了fieldname,则可以构建一个可以执行所需操作的视图。 您可以为表tb_modules添加一个触发器,只要修改tb_modules就会改变视图。这样,在进行查询时,您不需要从客户端使用动态sql。您唯一需要担心的是,在向tb_modules添加新行之前,需要在db中创建表
修改1 当然,触发器中的代码需要动态构建alter view语句。
编辑2 您还需要一个字段,其中包含有关tb_usertype和tb_religion等中哪些列与tb_value.extid(usertype_OR_religion_ID)连接的信息。或者您可以假设该字段将始终被称为id
编辑3 以下是在tb_module上构建触发器以改变视图v_values的方法。我在tb_modules中添加了fieldname作为列,我假设相关表中的id字段被称为id。
create trigger tb_modules_change on tb_modules after insert, delete, update
as
declare @sql nvarchar(max)
declare @moduleid int
declare @tablename varchar(50)
declare @fieldname varchar(50)
set @sql = 'alter view v_value as '
declare mcur cursor for
select id, tablename, fieldname
from tb_modules
open mcur
fetch next from mcur into @moduleid, @tablename, @fieldname
while @@FETCH_STATUS = 0
begin
set @sql = @sql + 'select tb_value.*, '+@tablename+'.'+@fieldname+' '+
'from tb_value '+
'inner join '+@tablename+' '+
'on tb_value.extid = '+@tablename+'.id '+
'where tb_value.tb_module_id = '+cast(@moduleid as varchar(10))
fetch next from mcur into @moduleid, @tablename, @fieldname
if @@FETCH_STATUS = 0
begin
set @sql = @sql + ' union all '
end
end
close mcur
deallocate mcur
exec sp_executesql @sql
答案 3 :(得分:1)
嗯..可能有更好的解决方案,但这是我的五美分:
SELECT
id,tb_modules_ID,usertype_OR_religion_ID,
COALESCE(
(SELECT TITLE FROM tb_usertype WHERE Id = usertype_OR_religion_ID),
(SELECT RELIGIONNAME FROM tb_religion WHERE Id = usertype_OR_religion_ID),
'N/A'
) AS SourceTable
FROM tb_valuehere
请注意,我现在无法检查语句,因此我保留自己的语法错误...
答案 4 :(得分:1)
首先,使用您当前的设计,唯一合理的解决方案是动态SQL。您应该在中间层编写一个模块,用于查询相应的表名并动态构建查询。试图在T-SQL中实现这一目标将是一场噩梦。 T-SQL不是为字符串构造而设计的。
正确的解决方案是构建一个正确设计的新数据库,迁移数据并废弃现有设计。您当前设计遇到的问题将会增长。新开发人员学习新系统将更加困难。它很容易出错。将没有数据完整性(例如,强制属性“开始日期”可被解析为日期)。自定义查询将是一件苦差事,等等。最后,根据当前的设计,您将很难在系统中获取所需的信息类型。
答案 5 :(得分:1)
首先将不受欢迎的人从后面带走,让他们摆脱苦难。他们伤害了人们。
由于功能不足,每次向模块添加行时,您都必须修改每个使用它的查询。适合www.dailywtf.com。
您也没有参照完整性,因为您无法在this_or_that列上定义FK。您的数据被暴露,可能是由同一个非自愿者编写的“代码”。毫无疑问,你知道这是造成死锁的地方。
这是一个“判断”,这是为了让你理解不受欢迎的严重性,并且你可以将其替换为你的经理。
SQL是为关系数据库设计的,这意味着规范化。这对错误的文件不利。当然,一些查询可能比其他查询更好(只看答案),但没有办法解决不需要的问题,任何SQL查询都会受到限制,并且每当添加模块行时都需要更改。
“动态”保留给数据库,不适用于扁平苍蝇。
两个答案。一个是阻止每次添加模块行时更改现有查询的持续愚蠢(欢迎您);第二个回答你的问题。
安全的未来查询
从现在开始,请确保修改当前使用非标签的所有查询,以便使用正确的视图。请勿使用视图进行更新/删除/插入。CREATE VIEW UserReligion_vw AS SELECT [XxxxId] = id, -- replace Xxxx [ReligionId] = usertype_OR_religion_ID FROM tb_value WHERE tb_modules_ID = 1
CREATE VIEW UserReligion_vw AS SELECT [XxxxId] = id, [ReligionId] = usertype_OR_religion_ID FROM tb_value WHERE tb_modules_ID = 2
<强>答案强>
好的,现在主要问题。我可以想到其他方法,但这个方法是最好的。您已经声明,您希望第三列也是鸡排泄物的非标准化部分,并且[EITHER_Religion_OR_UserType_OR_This_OR_That]的供应Title
。是的,所以你教导用户也要混淆;当模块没有增长时,他们将非常有趣地找出列包含的内容。是的,问题确实会自行复杂化。
SELECT [XxxxId] = id,
[Whatever] = CASE tb_modules_ID
WHEN 1 THEN ( SELECT name -- title, whatever
FROM tb_religion
WHERE id = V.usertype_OR_religion_ID
)
WHEN 2 THEN ( SELECT name -- title, whatever
FROM tb_usertype
WHERE id = V.usertype_OR_religion_ID
)
ELSE "(UnknownModule)" -- do not remove the brackets
END
FROM tb_value V
WHERE conditions... -- you need something here
这称为相关标量子查询。
它适用于4.9.2以来的任何Sybase版本,没有任何限制。和SQL 2005(我上次看,无论如何,2009年8月)。但是在MS上,如果tb_value
的体积很大,你将得到一个StackTrace,所以要确保WHERE
子句有一些条件。
但MS已经用他们的“新”2008年代码行破坏了服务器,因此它并不适用于所有情况(文件损坏越严重,工作的可能性越小;数据库设计越好,越多可能它会工作)。这就是为什么一些MS人每天为下一个服务包祈祷,而其他人从不参加教会。
答案 6 :(得分:1)
我猜你想要这样的东西:
将每个表中的表和一行添加到tb_modules中是很简单的。
SET NOCOUNT ON
if OBJECT_ID('tb_modules') > 0 drop table tb_modules;
if OBJECT_ID('tb_value') > 0 drop table tb_value;
if OBJECT_ID('tb_usertype') > 0 drop table tb_usertype;
if OBJECT_ID('tb_religion') > 0 drop table tb_religion;
go
create table dbo.tb_modules (
id int,
description varchar(20),
tablename varchar(255)
);
insert into tb_modules values ( 1, 'UserType', 'tb_usertype');
insert into tb_modules values ( 2, 'Religion', 'tb_religion');
create table dbo.tb_value(
id int,
tb_modules_ID int,
usertype_OR_religion_ID int
);
insert into tb_value values ( 1111, 1, 45);
insert into tb_value values ( 1112, 1, 55);
insert into tb_value values ( 1113, 2, 123);
insert into tb_value values ( 1114, 2, 234);
create table dbo.tb_usertype(
id int,
UserType varchar(30)
);
insert into tb_usertype values ( 45, 'User_type_45');
insert into tb_usertype values ( 55, 'User_type_55');
create table dbo.tb_religion(
id int,
Religion varchar(30)
);
insert into tb_religion values ( 123, 'Religion_123');
insert into tb_religion values ( 234, 'Religion_234');
-- start of query
declare @sql varchar(max) = null
Select @sql = case when @sql is null then ' ' else @sql + char(10) + 'union all ' end
+ 'Select ' + str(id) + ' type, id, ' + description + ' description from ' + tablename from tb_modules
set @sql = 'select v.id, tb_modules_ID , usertype_OR_religion_ID , t.description
from tb_value v
join ( ' + @sql + ') as t
on v.tb_modules_ID = t.type and v.usertype_OR_religion_ID = t.id
'
Print @sql
exec( @sql)
答案 7 :(得分:-1)
我认为它打算与动态sql一起使用。
可能会将每个tb_value.tb_modules_ID行分解为自己的临时表,以tb_modules.tablename命名。
然后让sp迭代通过临时表来匹配你的命名约定(通过前缀或后缀)构建sql并进行连接。