如何更新 table 以在数字的固定位置插入小数点?
How can I update a table to insert decimal points at a fixed position in numbers?
我正在使用 Microsoft SQL Server 2014,并且有一个包含三列的 table,字段数据类型是 Decimal(38,0)
。
我想更新 table 的每一行以在前两位数字后插入一个小数点。比如我想让123456
变成12.3456
。数字长度不同;有些是五位数,有些是七位数,等等
我的table是:
+-------------+-------+-------+
| ID | X | Y |
+-------------+-------+-------+
| 1200 | 321121| 345000|
| 1201 | 564777| 4145 |
| 1202 | 4567 | 121444|
| 1203 | 12747 | 789887|
| 1204 | 489899| 124778|
+-------------+-------+-------+
我想将其更改为:
+-------------+--------+--------+
| ID | X | Y |
+-------------+--------+--------+
| 1200 | 32.1121| 34.5000|
| 1201 | 56.4777| 41.45 |
| 1202 | 45.67 | 12.1444|
| 1203 | 12.747 | 78.9887|
| 1204 | 48.9899| 12.4778|
+-------------+--------+--------+
我的代码是:
Update [dbo].[UTM]
SET [X] = STUFF([X],3,0,'.')
[Y] = STUFF([X],3,0,'.')
我试过这个:
BEGIN
DECLARE @COUNT1 int;
DECLARE @COUNT2 int;
DECLARE @TEMP_X VARCHAR(255);
DECLARE @TEMP_Y VARCHAR(255);
DECLARE @TEMP_main VARCHAR(255);
SELECT @COUNT1 = COUNT(*) FROM [UTM];
SET @COUNT2 = 0;
WHILE(@COUNT2<@COUNT1)
BEGIN
SET @TEMP_main = (SELECT [id] from [UTM] order by [id] desc offset @COUNT2 rows fetch next 1 rows only);
SET @TEMP_X = (SELECT [X] from [UTM] order by [id] desc offset @COUNT2 rows fetch next 1 rows only);
SET @TEMP_Y = (SELECT [Y] from [UTM] order by [id] desc offset @COUNT2 rows fetch next 1 rows only);
UPDATE [dbo].[UTM]
SET [X] = CONVERT(decimal(38,0),STUFF(@TEMP_X,3,0,'.'))
,[Y] = CONVERT(decimal(38,0),STUFF(@TEMP_Y,3,0,'.'))
WHERE [id] = @TEMP_main;
SET @COUNT2 = @COUNT2 + 1
END
END
只需在 update
:
中执行此操作
Update [dbo].[UTM]
SET X = STUFF(CONVERT(VARCHAR(255), X), 3, 0, '.'),
Y = STUFF(CONVERT(VARCHAR(255), X), 3, 0, '.');
值被转换为字符串,但字符串将被隐式转换回 X
和 Y
的任何类型。如果类型不兼容,您可能会遇到错误。
如果您有负值,则应将它们包含在同一数据中。这是使用 case
:
处理的
Update [dbo].[UTM]
SET X = STUFF(CONVERT(VARCHAR(255), X), (CASE WHEN X < 0 THEN 4 ELSE 3 END), 0, '.'),
Y = STUFF(CONVERT(VARCHAR(255), X), (CASE WHEN X < 0 THEN 4 ELSE 3 END), 0, '.');
尝试以下更新:
UPDATE UTM
SET
X = CAST(X AS DECIMAL(10,2)) / POWER(10, LEN(CAST(ABS(X) AS VARCHAR(10)))-2),
Y = CAST(Y AS DECIMAL(10,2)) / POWER(10, LEN(CAST(ABS(Y) AS VARCHAR(10)))-2);
这里的逻辑是将每个数字除以 10 的长度减 2 次方。这对正数和负数都适用,因为我们使用数字的绝对值进行归一化。按照下面的 link 进行 运行 演示。
这是基于先前删除的 post 的假设(您也有负数)。
首先,当您使用 decimal(38,0)
时,您无法以任何精度存储值,因此您还需要更改数据类型。这提供了您似乎正在寻找的结果:
USE Sandbox;
GO
CREATE TABLE dbo.SampleTable (ID int,
X decimal(38,0),
Y decimal(38,0));
INSERT INTO dbo.SampleTable (ID,
X,
Y)
VALUES (1200,321121,345000),
(1201,564777,4145 ),
(1202,4567 ,121444),
(1203,12747 ,789887),
(1204,489899,124778),
(1205,-32472,-27921);
GO
--Fix the datatype
ALTER TABLE dbo.SampleTable ALTER COLUMN X decimal(10,4); --Based on data provided, may need larger scale
ALTER TABLE dbo.SampleTable ALTER COLUMN Y decimal(10,4); --Based on data provided, may need larger scale
GO
--update the data
UPDATE dbo.SampleTable
SET X = STUFF(ABS(CONVERT(int,X)),3,0,'.') * CONVERT(decimal(10,4),CASE WHEN X < 0 THEN -1.0 ELSE 1.0 END),
Y = STUFF(ABS(CONVERT(int,Y)),3,0,'.') * CONVERT(decimal(10,4),CASE WHEN Y < 0 THEN -1.0 ELSE 1.0 END);
SELECT *
FROM dbo.SampleTable;
GO
DROP TABLE dbo.SampleTable;
请注意,您不会得到像 41.45
这样的值,而是 41.4500
。如果您不想显示尾随 0,则需要在表示层中进行格式化(否则您必须将值存储为 varchar
,这是一个非常糟糕的主意)。
您可以使用 FLOOR(LOG10(num) + 1)
和 POWER(10, num_digits)
获得数字中的位数来确定要除以的数字。完全没有字符串操作:
DECLARE @t TABLE (ID INT, X DECIMAL(38, 0), Y DECIMAL(38, 0));
INSERT INTO @t VALUES
(1200, 321121, 345000),
(1201, 564777, 4145),
(1202, 4567, 121444),
(1203, 12747, 789887),
(1204, 489899, 124778);
SELECT ID
, X, X / POWER(10, FLOOR(LOG10(ABS(X))) + 1 - 2) AS X2
, Y, Y / POWER(10, FLOOR(LOG10(ABS(Y))) + 1 - 2) As Y2
FROM @t
您可以轻松扩展它以处理具有小数部分的值:
DECLARE @t TABLE (X DECIMAL(38, 8));
INSERT INTO @t VALUES
( 12345.00000),
( 12345.67890),
(-12345.00000),
(-12345.67890);
SELECT X, CASE
WHEN XS >= 0 THEN X / POWER(10, XS)
ELSE X * POWER(10, -XS)
END X2
FROM @t
CROSS APPLY (SELECT FLOOR(LOG10(ABS(X))) + 1 - 2 AS XS) AS CA
输出:
| X | X2 |
|-----------------|------------|
| 12345.00000000 | 12.345000 |
| 12345.67890000 | 12.345679 |
| -12345.00000000 | -12.345000 |
| -12345.67890000 | -12.345679 |
我正在使用 Microsoft SQL Server 2014,并且有一个包含三列的 table,字段数据类型是 Decimal(38,0)
。
我想更新 table 的每一行以在前两位数字后插入一个小数点。比如我想让123456
变成12.3456
。数字长度不同;有些是五位数,有些是七位数,等等
我的table是:
+-------------+-------+-------+
| ID | X | Y |
+-------------+-------+-------+
| 1200 | 321121| 345000|
| 1201 | 564777| 4145 |
| 1202 | 4567 | 121444|
| 1203 | 12747 | 789887|
| 1204 | 489899| 124778|
+-------------+-------+-------+
我想将其更改为:
+-------------+--------+--------+
| ID | X | Y |
+-------------+--------+--------+
| 1200 | 32.1121| 34.5000|
| 1201 | 56.4777| 41.45 |
| 1202 | 45.67 | 12.1444|
| 1203 | 12.747 | 78.9887|
| 1204 | 48.9899| 12.4778|
+-------------+--------+--------+
我的代码是:
Update [dbo].[UTM]
SET [X] = STUFF([X],3,0,'.')
[Y] = STUFF([X],3,0,'.')
我试过这个:
BEGIN
DECLARE @COUNT1 int;
DECLARE @COUNT2 int;
DECLARE @TEMP_X VARCHAR(255);
DECLARE @TEMP_Y VARCHAR(255);
DECLARE @TEMP_main VARCHAR(255);
SELECT @COUNT1 = COUNT(*) FROM [UTM];
SET @COUNT2 = 0;
WHILE(@COUNT2<@COUNT1)
BEGIN
SET @TEMP_main = (SELECT [id] from [UTM] order by [id] desc offset @COUNT2 rows fetch next 1 rows only);
SET @TEMP_X = (SELECT [X] from [UTM] order by [id] desc offset @COUNT2 rows fetch next 1 rows only);
SET @TEMP_Y = (SELECT [Y] from [UTM] order by [id] desc offset @COUNT2 rows fetch next 1 rows only);
UPDATE [dbo].[UTM]
SET [X] = CONVERT(decimal(38,0),STUFF(@TEMP_X,3,0,'.'))
,[Y] = CONVERT(decimal(38,0),STUFF(@TEMP_Y,3,0,'.'))
WHERE [id] = @TEMP_main;
SET @COUNT2 = @COUNT2 + 1
END
END
只需在 update
:
Update [dbo].[UTM]
SET X = STUFF(CONVERT(VARCHAR(255), X), 3, 0, '.'),
Y = STUFF(CONVERT(VARCHAR(255), X), 3, 0, '.');
值被转换为字符串,但字符串将被隐式转换回 X
和 Y
的任何类型。如果类型不兼容,您可能会遇到错误。
如果您有负值,则应将它们包含在同一数据中。这是使用 case
:
Update [dbo].[UTM]
SET X = STUFF(CONVERT(VARCHAR(255), X), (CASE WHEN X < 0 THEN 4 ELSE 3 END), 0, '.'),
Y = STUFF(CONVERT(VARCHAR(255), X), (CASE WHEN X < 0 THEN 4 ELSE 3 END), 0, '.');
尝试以下更新:
UPDATE UTM
SET
X = CAST(X AS DECIMAL(10,2)) / POWER(10, LEN(CAST(ABS(X) AS VARCHAR(10)))-2),
Y = CAST(Y AS DECIMAL(10,2)) / POWER(10, LEN(CAST(ABS(Y) AS VARCHAR(10)))-2);
这里的逻辑是将每个数字除以 10 的长度减 2 次方。这对正数和负数都适用,因为我们使用数字的绝对值进行归一化。按照下面的 link 进行 运行 演示。
这是基于先前删除的 post 的假设(您也有负数)。
首先,当您使用 decimal(38,0)
时,您无法以任何精度存储值,因此您还需要更改数据类型。这提供了您似乎正在寻找的结果:
USE Sandbox;
GO
CREATE TABLE dbo.SampleTable (ID int,
X decimal(38,0),
Y decimal(38,0));
INSERT INTO dbo.SampleTable (ID,
X,
Y)
VALUES (1200,321121,345000),
(1201,564777,4145 ),
(1202,4567 ,121444),
(1203,12747 ,789887),
(1204,489899,124778),
(1205,-32472,-27921);
GO
--Fix the datatype
ALTER TABLE dbo.SampleTable ALTER COLUMN X decimal(10,4); --Based on data provided, may need larger scale
ALTER TABLE dbo.SampleTable ALTER COLUMN Y decimal(10,4); --Based on data provided, may need larger scale
GO
--update the data
UPDATE dbo.SampleTable
SET X = STUFF(ABS(CONVERT(int,X)),3,0,'.') * CONVERT(decimal(10,4),CASE WHEN X < 0 THEN -1.0 ELSE 1.0 END),
Y = STUFF(ABS(CONVERT(int,Y)),3,0,'.') * CONVERT(decimal(10,4),CASE WHEN Y < 0 THEN -1.0 ELSE 1.0 END);
SELECT *
FROM dbo.SampleTable;
GO
DROP TABLE dbo.SampleTable;
请注意,您不会得到像 41.45
这样的值,而是 41.4500
。如果您不想显示尾随 0,则需要在表示层中进行格式化(否则您必须将值存储为 varchar
,这是一个非常糟糕的主意)。
您可以使用 FLOOR(LOG10(num) + 1)
和 POWER(10, num_digits)
获得数字中的位数来确定要除以的数字。完全没有字符串操作:
DECLARE @t TABLE (ID INT, X DECIMAL(38, 0), Y DECIMAL(38, 0));
INSERT INTO @t VALUES
(1200, 321121, 345000),
(1201, 564777, 4145),
(1202, 4567, 121444),
(1203, 12747, 789887),
(1204, 489899, 124778);
SELECT ID
, X, X / POWER(10, FLOOR(LOG10(ABS(X))) + 1 - 2) AS X2
, Y, Y / POWER(10, FLOOR(LOG10(ABS(Y))) + 1 - 2) As Y2
FROM @t
您可以轻松扩展它以处理具有小数部分的值:
DECLARE @t TABLE (X DECIMAL(38, 8));
INSERT INTO @t VALUES
( 12345.00000),
( 12345.67890),
(-12345.00000),
(-12345.67890);
SELECT X, CASE
WHEN XS >= 0 THEN X / POWER(10, XS)
ELSE X * POWER(10, -XS)
END X2
FROM @t
CROSS APPLY (SELECT FLOOR(LOG10(ABS(X))) + 1 - 2 AS XS) AS CA
输出:
| X | X2 |
|-----------------|------------|
| 12345.00000000 | 12.345000 |
| 12345.67890000 | 12.345679 |
| -12345.00000000 | -12.345000 |
| -12345.67890000 | -12.345679 |