web-dev-qa-db-fra.com

Comment puis-je mettre à jour un tableau pour insérer des décimales à une position fixe en nombre?

J'utilise Microsoft SQL Server 2014 et j'ai une table avec trois colonnes et le type de données de champ est Decimal(38,0).

Je veux mettre à jour chaque ligne de ma table pour insérer un point décimal après les deux premiers chiffres. Par exemple, je veux 123456 devenir 12.3456. Les nombres sont de longueurs différentes; certains sont à cinq chiffres, certains sont à sept chiffres, etc.

Ma table est:

+-------------+-------+-------+
| ID          |   X   |   Y   |
+-------------+-------+-------+
| 1200        | 321121| 345000|
| 1201        | 564777| 4145  |
| 1202        | 4567  | 121444|
| 1203        | 12747 | 789887|
| 1204        | 489899| 124778|
+-------------+-------+-------+

Et je veux changer cela en:

+-------------+--------+--------+
| 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|
+-------------+--------+--------+

Mon code est:

Update [dbo].[UTM]
     SET [X] = STUFF([X],3,0,'.')
         [Y] = STUFF([X],3,0,'.')

Et j'ai essayé ceci:

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
14
Javad Abedi

Cela fonctionne sur l'hypothèse d'une publication précédemment supprimée (que vous avez également un nombre négatif).

Tout d'abord, comme vous utilisez une decimal(38,0), vous ne pouvez pas stocker de valeurs avec une précision quelconque, vous devez donc également changer le type de données. Cela donne les résultats que vous recherchez:

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;

Notez que vous n'obtiendrez pas une valeur comme 41.45, mais plutôt 41.4500. Si vous ne voulez pas afficher les 0 à la fin, vous devez effectuer la mise en forme dans votre couche de présentation (sinon vous devrez stocker les valeurs sous la forme d'un varchar, et c'est une très mauvaise idée).

8
Larnu

Essayez la mise à jour suivante:

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);

La logique ici est de diviser chaque nombre par 10 à la puissance de la longueur du nombre moins 2. Cela fonctionne pour les nombres positifs et négatifs, car nous utilisons la valeur absolue du nombre pour la normalisation. Suivez le lien ci-dessous pour une démonstration en cours.

enter image description here

Démo

10
Tim Biegeleisen

Faites-le dans le update:

Update [dbo].[UTM]
     SET X = STUFF(CONVERT(VARCHAR(255), X), 3, 0, '.'),
         Y = STUFF(CONVERT(VARCHAR(255), X), 3, 0, '.');

Les valeurs sont converties en chaînes, mais les chaînes seront reconverties implicitement en tout type X et Y. Vous pouvez obtenir une erreur si les types ne sont pas compatibles.

Si vous avez des valeurs négatives, vous devez les inclure dans les mêmes données. Ceci est géré à l'aide de 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, '.');
7
Gordon Linoff

Vous pouvez obtenir le nombre de chiffres d'un nombre en utilisant FLOOR(LOG10(num) + 1) et POWER(10, num_digits) pour déterminer le nombre à diviser. Aucune opération de chaîne du tout:

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

Vous pouvez facilement l'étendre pour gérer les valeurs qui ont une partie décimale:

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

Production:

| X               | X2         |
|-----------------|------------|
|  12345.00000000 |  12.345000 |
|  12345.67890000 |  12.345679 |
| -12345.00000000 | -12.345000 |
| -12345.67890000 | -12.345679 |

Démo sur db <> violon

3
Salman A