同一字段多ID存储名称映射

本文探讨了在数据库设计中处理1对多关系的有效方法。通过对比两种不同的数据存储方式,详细介绍了如何利用SQL查询来高效地展示多对多关系中的具体信息。

在数据库设计时,为了减少表存储的记录数,对于1对多的关系可以存储在同一个记录中,例如某一个应用会被多个人使用,有一种存储方法如下:


这样会造成记录数会越来越多,还有一种方法可以用2条记录存储上述数据:

第一种方法的好处就是显示员工名称非常方便,和员工信息表关联即可;第二种方法如果要显示维护人员的姓名就非常麻烦,例如我们有下面的两张表:

if object_id('[emp]') is not null drop table [emp]
go 
create table [emp]([员工id] varchar(3),[姓名] varchar(4))
insert [emp]
select '001','张三' union all
select '002','李四' union all
select '003','XXX'
 
if object_id('[app]') is not null drop table [app]
go 
create table [app]([应用id] varchar(6),[应用名] varchar(5),[维护员工id] varchar(11))
insert [app]
select 'APP001','应用a','001,002,003' union all
select 'APP002','应用b','002,003'

要求的结果是对于app表显示维护员工的姓名,那么可以将问题分解,一步步求解。对于“001,002,003”这个如果要显示其名称,那么可以用下面的语句:

select ','+e.[姓名] from emp e 
where charindex(','+e.[员工id]+',',','+'001,002,003'+',')>0
for xml path('')

得到的结果如下

如果要对app表中的每一条记录都实现这样的结果怎么做呢

select distinct
       [应用id],
       [应用名],
       [维护员工id],
       stuff((select ','+e.[姓名] from emp e 
              where charindex(','+e.[员工id]+',',','+t.维护员工id+',')>0
              for xml path('')
            ),1,1,'') as 维护员工姓名
from app t

最终结果




<think>我们有一个需求:将包含不同单位的字段转换为统一单位。例如,有些数据以“千”为单位,有些以“百”为单位,我们需要将它们统一转换为“元”为单位(或其他单位)。 根据引用[1]中的思路,我们可以使用CASE语句来识别单位并进行转换。但注意,引用[1]是将数字转换为带中文单位的字符串,而我们现在需要的是将带单位的字符串转换为统一单位的数字(或字符串)。 假设我们的数据表中有一个字段,其值可能是: '1.5千' -> 表示1500元 '20百' -> 表示2000元 '3000' -> 表示3000元(没有单位,直接是元) 我们需要将这些值都转换为以“元”为单位的数值,或者转换为以“千元”为单位的数值等。 步骤: 1. 识别单位:我们可以通过字符串中是否包含“千”、“百”等字符来判断单位。 2. 提取数字部分:将字符串中的数字部分提取出来。 3. 根据单位进行换算:如果是“千”,则乘以1000;如果是“百”,则乘以100;如果没有单位,则直接使用。 但是,注意原始数据可能是字符串,并且格式可能不统一。因此,我们需要先分离数字和单位。 我们可以使用SQL的字符串函数(如SUBSTRING、PATINDEX、CHARINDEX等,具体函数取决于数据库系统)来提取数字和单位。 由于不同的数据库系统(如MySQLSQL Server)函数不同,这里我们分别以MySQLSQL Server为例。 假设我们有一个表`salary_data`,其中有一个字段`amount`,存储的是带单位的字符串。 目标:将`amount`转换为以“元”为单位的数值。 示例数据: | id | amount | |----|---------| | 1 | 1.5千 | | 2 | 20百 | | 3 | 3000 | 转换后希望得到: | id | amount | amount_in_yuan | |----|---------|----------------| | 1 | 1.5千 | 1500 | | 2 | 20百 | 2000 | | 3 | 3000 | 3000 | 方法: 在MySQL中,我们可以使用如下方式: 步骤: 1. 提取数字部分:我们可以使用正则表达式或者字符串函数。但MySQL的字符串函数不支持正则表达式提取(在MySQL 8.0之前),我们可以使用SUBSTRING_INDEX或者LOCATE等函数。 2. 判断单位:检查字符串中是否包含'千'或'百'。 由于没有现成的正则提取,我们可以这样: - 找到第一个非数字(非小数点)的字符,然后截取前面的部分作为数字。 但是,如果字符串中没有单位(如'3000'),则直接转换。 我们可以写一个CASE表达式,分别判断是否包含'千'、'百',然后分别处理。 例如: ```sql SELECT id, amount, CASE WHEN amount LIKE '%千' THEN -- 提取数字部分:去掉最后的'千',然后转换为数字,再乘以1000 CAST(REPLACE(amount, '千', '') AS DECIMAL(10, 2)) * 1000 WHEN amount LIKE '%百' THEN CAST(REPLACE(amount, '百', '') AS DECIMAL(10, 2)) * 100 ELSE -- 如果没有单位,直接转换 CAST(amount AS DECIMAL(10, 2)) END AS amount_in_yuan FROM salary_data; ``` 但是,这种方法假设单位只出现在最后,并且字符串中没有其他非数字字符(除了单位)。如果数据不规范,比如可能有空格,我们可以先使用TRIM函数。 改进:考虑去除空格,并允许单位前有空格。 我们可以使用TRIM,并且在替换单位时,先去掉空格: ```sql SELECT id, amount, CASE WHEN TRIM(amount) LIKE '%千' THEN CAST(TRIM(REPLACE(amount, '千', '')) AS DECIMAL(10, 2)) * 1000 WHEN TRIM(amount) LIKE '%百' THEN CAST(TRIM(REPLACE(amount, '百', '')) AS DECIMAL(10, 2)) * 100 ELSE CAST(TRIM(amount) AS DECIMAL(10, 2)) END AS amount_in_yuan FROM salary_data; ``` 但是,这样替换可能会误伤中间有“千”或“百”的情况,比如“一千元”这样的字符串,但我们的数据是像“1.5千”这样的,所以问题不大。但为了更准确,我们可以只替换最后出现的单位。 另一种方法是:先提取除最后一个字符以外的部分,然后判断最后一个字符是否是单位。 在MySQL中,我们可以使用SUBSTRING和LENGTH函数: ```sql SELECT id, amount, CASE WHEN RIGHT(TRIM(amount),1) = '千' THEN CAST(LEFT(TRIM(amount), LENGTH(TRIM(amount))-1) AS DECIMAL(10, 2)) * 1000 WHEN RIGHT(TRIM(amount),1) = '百' THEN CAST(LEFT(TRIM(amount), LENGTH(TRIM(amount))-1) AS DECIMAL(10, 2)) * 100 ELSE CAST(TRIM(amount) AS DECIMAL(10, 2)) END AS amount_in_yuan FROM salary_data; ``` 这样就更准确了。 在SQL Server中,我们可以使用类似的方法: ```sql SELECT id, amount, CASE WHEN RIGHT(RTRIM(LTRIM(amount)),1) = '千' THEN CAST(LEFT(RTRIM(LTRIM(amount)), LEN(RTRIM(LTRIM(amount)))-1) AS DECIMAL(10, 2)) * 1000 WHEN RIGHT(RTRIM(LTRIM(amount)),1) = '百' THEN CAST(LEFT(RTRIM(LTRIM(amount)), LEN(RTRIM(LTRIM(amount)))-1) AS DECIMAL(10, 2)) * 100 ELSE CAST(RTRIM(LTRIM(amount)) AS DECIMAL(10, 2)) END AS amount_in_yuan FROM salary_data; ``` 但是,注意:如果有些数据没有单位,但最后有空格,那么TRIM后最后一位是数字,所以不会进入单位分支。 另外,如果单位是两个字符(比如“万元”),那么我们需要调整。根据实际数据情况调整。 假设我们还有“万元”的情况,那么我们需要判断最后两个字符。我们可以这样: 先去掉空格,然后判断最后两个字符,如果最后两个字符是'万元',则单位是'万',乘以10000。然后判断最后一个字符是'千'或'百'。 因此,我们可以使用嵌套CASE: 在MySQL中: ```sql SELECT id, amount, CASE WHEN RIGHT(TRIM(amount),2) = '万元' THEN CAST(LEFT(TRIM(amount), LENGTH(TRIM(amount))-2) AS DECIMAL(10, 2)) * 10000 WHEN RIGHT(TRIM(amount),1) = '千' THEN CAST(LEFT(TRIM(amount), LENGTH(TRIM(amount))-1) AS DECIMAL(10, 2)) * 1000 WHEN RIGHT(TRIM(amount),1) = '百' THEN CAST(LEFT(TRIM(amount), LENGTH(TRIM(amount))-1) AS DECIMAL(10, 2)) * 100 ELSE CAST(TRIM(amount) AS DECIMAL(10, 2)) END AS amount_in_yuan FROM salary_data; ``` 注意:这里我们假设单位只能是“万元”、“千”、“百”中的一种,并且单位在最后。 如果数据中可能包含种单位,或者单位位置不固定,那么可能需要更复杂的处理,比如正则表达式。在MySQL 8.0+和SQL Server中,我们可以使用正则表达式函数(如REGEXP_SUBSTR)来提取数字和单位,但这里不展开。 另外,注意转换后的数值精度问题,我们使用DECIMAL(10,2)表示最10位,小数部分2位。 如果原始数据中数字部分不是合法的数字格式(比如包含逗号等),我们需要先去除逗号等非数字字符(除了小数点和负号)。这会使问题更复杂,但根据引用[2]和[3]的思路,我们可以使用替换函数。 例如,如果数字部分有逗号,我们可以先去掉逗号: 在MySQL中,我们可以先提取数字字符串,然后去掉逗号,再转换。但是,在我们的CASE语句中,在提取数字字符串后,我们可以使用REPLACE函数: ```sql ... CASE WHEN ... THEN CAST(REPLACE(LEFT(TRIM(amount), LENGTH(TRIM(amount))-1), ',', '') AS DECIMAL(10, 2)) * 1000 ... ``` 但注意,我们只去掉了数字部分中的逗号。 总结:根据实际数据的复杂程度,可能需要步处理。以上方法适用于单位在最后且数字部分不包含其他非数字字符(除了小数点和负号)的情况。 如果单位不在最后,或者单位与数字之间有空格,那么我们需要更灵活的方法。例如,我们可以提取所有非数字非小数点的部分作为单位,但这样比较复杂。在实际应用中,建议先对数据进行清洗,或者使用更强大的字符串处理函数(如正则表达式)来提取。 如果数据库支持正则表达式(如MySQL 8.0+,SQL Server 2017+),我们可以使用正则表达式来提取数字和单位。这里以MySQL 8.0为例: ```sql SELECT id, amount, CASE WHEN REGEXP_SUBSTR(amount, '[^0-9.]+') = '千' THEN CAST(REGEXP_SUBSTR(amount, '[0-9.]+') AS DECIMAL(10,2)) * 1000 WHEN REGEXP_SUBSTR(amount, '[^0-9.]+') = '百' THEN CAST(REGEXP_SUBSTR(amount, '[0-9.]+') AS DECIMAL(10,2)) * 100 WHEN REGEXP_SUBSTR(amount, '[^0-9.]+') = '万元' THEN CAST(REGEXP_SUBSTR(amount, '[0-9.]+') AS DECIMAL(10,2)) * 10000 ELSE CAST(amount AS DECIMAL(10,2)) END AS amount_in_yuan FROM salary_data; ``` 注意:正则表达式提取的单位是第一个非数字非小数点的连续字符串。如果字符串中有个非数字部分,可能不准确。所以这种方法适用于单位出现在数字后面且连续的情况。 综上所述,具体实现取决于数据的具体格式和使用的数据库系统。 根据用户的问题,我们提供了几种方法。用户可以根据自己的数据库系统和数据特点选择合适的方法。 另外,如果用户希望转换为其他单位(比如统一转换为“千元”),则可以在上述结果的基础上再除以1000。 例如,统一转换为千元: ```sql SELECT ... (上面的表达式) / 1000 AS amount_in_thousand_yuan ... ``` 最后,注意异常处理:如果转换失败(比如字符串不符合预期),应该返回什么?可以使用TRY_CAST(在支持的数据库中)或忽略错误。 在MySQL中,我们可以使用CAST,如果转换失败会报错,可以使用IFNULL或NULLIF来处理,但更安全的方式是使用正则表达式确保数字部分有效。 由于问题复杂,建议在应用层进行数据清洗和转换,或者在数据库中使用存储过程。 以上方法仅供参考,实际应用中应进行充分测试。</think>### SQL 将不同单位的数据字段转换为统一单位 在 SQL 中将包含不同单位的字段转换为统一单位,需要根据具体单位进行数学转换,并处理字符串拼接问题。以下是详细方法和示例: #### 核心步骤 1. **识别单位**:通过字符串匹配判断单位(如"千"、"百"等) 2. **提取数值**:分离数字部分和单位标识 3. **单位换算**:根据单位进行数学转换 4. **结果拼接**:将转换后的数值与目标单位组合 #### 示例解决方案(MySQL) 假设表 `financial_data` 包含带单位的金额字段 `amount`: | id | amount | |----|---------| | 1 | 1.5千 | | 2 | 20百 | | 3 | 3000 | ```sql SELECT id, amount AS 原始值, CASE WHEN amount LIKE '%千' THEN CONCAT(CAST(REPLACE(amount, '千', '') AS DECIMAL) * 1000, '元') WHEN amount LIKE '%百' THEN CONCAT(CAST(REPLACE(amount, '百', '') AS DECIMAL) * 100, '元') ELSE CONCAT(amount, '元') -- 无单位直接拼接 END AS 统一单位值 FROM financial_data; ``` **输出结果**: | id | 原始值 | 统一单位值 | |----|--------|------------| | 1 | 1.5千 | 1500元 | | 2 | 20百 | 2000元 | | 3 | 3000 | 3000元 | #### 高级处理技巧 1. **处理混合单位**(如"万元"): ```sql WHEN amount LIKE '%万%' THEN CONCAT(CAST(REPLACE(amount, '万', '') AS DECIMAL) * 10000, '元') ``` 2. **保留小数精度**: ```sql CONCAT(ROUND(CAST(... AS DECIMAL) * 1000, 2), '元') ``` 3. **动态列转换**(SQL Server): ```sql DECLARE @sql NVARCHAR(MAX) = '' SELECT @sql += ', CONVERT(DECIMAL(10,2), ' + column_name + ') * ' + CASE WHEN unit = '千' THEN '1000' WHEN unit = '百' THEN '100' END FROM sys.columns SET @sql = 'SELECT ' + STUFF(@sql,1,1,'') + ' FROM table' EXEC sp_executesql @sql ``` #### 注意事项 1. **数据清洗**:使用 `TRIM()` 处理空格 2. **错误处理**:添加 `TRY_CAST()`(SQL Server)或正则表达式验证 3. **性能优化**:对大表创建计算列或物化视图 4. **单位映射表**:可创建单位换算表实现动态关联: ```sql SELECT f.amount, f.value * u.conversion_rate AS 统一值 FROM financial_data f JOIN unit_conversion u ON f.unit = u.unit_name ``` > **关键点**:单位转换本质是数值计算,需先分离数值和单位标识。不同数据库语法差异主要在字符串处理函数(MySQL用`SUBSTRING_INDEX`,SQL Server用`SUBSTRING`)[^1][^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值