Sqlserver
 sql >> डेटाबेस >  >> RDS >> Sqlserver

लॉग और क्स्प कार्यों में गोलाई मुद्दा

शुद्ध T-SQL में LOG और EXP float . के साथ काम करें प्रकार (8 बाइट्स), जिसमें केवल 15-17 महत्वपूर्ण अंक हैं . यहां तक ​​​​कि अगर आप पर्याप्त बड़े मान जोड़ते हैं तो वह अंतिम 15 वां अंक गलत हो सकता है। आपका डेटा numeric(22,6) . है , इसलिए 15 सार्थक अंक पर्याप्त नहीं हैं।

POWER numeric लौटा सकते हैं संभावित रूप से उच्च परिशुद्धता के साथ टाइप करें, लेकिन यह हमारे लिए बहुत कम उपयोग है, क्योंकि दोनों LOG और LOG10 केवल float लौटा सकते हैं वैसे भी।

समस्या को प्रदर्शित करने के लिए मैं आपके उदाहरण के प्रकार को numeric(15,0) . में बदल दूंगा और POWER का उपयोग करें EXP . के बजाय :

DECLARE @TEST TABLE
  (
     PAR_COLUMN INT,
     PERIOD     INT,
     VALUE      NUMERIC(15, 0)
  );

INSERT INTO @TEST VALUES 
(1,601,10 ),
(1,602,20 ),
(1,603,30 ),
(1,604,40 ),
(1,605,50 ),
(1,606,60 ),
(2,601,100),
(2,602,200),
(2,603,300),
(2,604,400),
(2,605,500),
(2,606,600);

SELECT *,
    POWER(CAST(10 AS numeric(15,0)),
        Sum(LOG10(
            Abs(NULLIF(VALUE, 0))
            ))
        OVER(PARTITION BY PAR_COLUMN ORDER BY PERIOD)) AS Mul
FROM @TEST;

परिणाम

+------------+--------+-------+-----------------+
| PAR_COLUMN | PERIOD | VALUE |       Mul       |
+------------+--------+-------+-----------------+
|          1 |    601 |    10 |              10 |
|          1 |    602 |    20 |             200 |
|          1 |    603 |    30 |            6000 |
|          1 |    604 |    40 |          240000 |
|          1 |    605 |    50 |        12000000 |
|          1 |    606 |    60 |       720000000 |
|          2 |    601 |   100 |             100 |
|          2 |    602 |   200 |           20000 |
|          2 |    603 |   300 |         6000000 |
|          2 |    604 |   400 |      2400000000 |
|          2 |    605 |   500 |   1200000000000 |
|          2 |    606 |   600 | 720000000000001 |
+------------+--------+-------+-----------------+

यहां प्रत्येक चरण सटीकता खो देता है। लॉग की गणना सटीकता खो देती है, एसयूएम परिशुद्धता खो देता है, EXक्स्प/पावर परिशुद्धता खो देता है। इन अंतर्निहित कार्यों के साथ मुझे नहीं लगता कि आप इसके बारे में बहुत कुछ कर सकते हैं।

तो, इसका उत्तर है - C# के साथ CLR का उपयोग करें decimal टाइप करें (double नहीं) ), जो उच्च परिशुद्धता (28-29 महत्वपूर्ण अंक) का समर्थन करता है। आपका मूल SQL प्रकार numeric(22,6) उसमें फिट होगा। और आपको LOG/EXP . के साथ ट्रिक की आवश्यकता नहीं होगी ।

उफ़। मैंने एक सीएलआर समुच्चय बनाने की कोशिश की जो उत्पाद की गणना करता है। यह मेरे परीक्षणों में काम करता है, लेकिन केवल एक साधारण समुच्चय के रूप में, अर्थात

यह काम करता है:

SELECT T.PAR_COLUMN, [dbo].[Product](T.VALUE) AS P
FROM @TEST AS T
GROUP BY T.PAR_COLUMN;

और यहां तक ​​कि OVER (PARTITION BY) काम करता है:

SELECT *,
    [dbo].[Product](T.VALUE) 
    OVER (PARTITION BY PAR_COLUMN) AS P
FROM @TEST AS T;

लेकिन, OVER (PARTITION BY ... ORDER BY ...) का उपयोग करके उत्पाद चलाना काम नहीं करता (एसक्यूएल सर्वर 2014 एक्सप्रेस 12.0.2000.8 के साथ जांचा गया):

SELECT *,
    [dbo].[Product](T.VALUE) 
    OVER (PARTITION BY T.PAR_COLUMN ORDER BY T.PERIOD 
          ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS CUM_MUL
FROM @TEST AS T;

एक खोज में यह कनेक्ट आइटम मिला। , जो "ठीक नहीं होगा" के रूप में बंद है और यह प्रश्न

सी# कोड:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.IO;
using System.Collections.Generic;
using System.Text;

namespace RunningProduct
{
    [Serializable]
    [SqlUserDefinedAggregate(
        Format.UserDefined,
        MaxByteSize = 17,
        IsInvariantToNulls = true,
        IsInvariantToDuplicates = false,
        IsInvariantToOrder = true,
        IsNullIfEmpty = true)]
    public struct Product : IBinarySerialize
    {
        private bool m_bIsNull; // 1 byte storage
        private decimal m_Product; // 16 bytes storage

        public void Init()
        {
            this.m_bIsNull = true;
            this.m_Product = 1;
        }

        public void Accumulate(
            [SqlFacet(Precision = 22, Scale = 6)] SqlDecimal ParamValue)
        {
            if (ParamValue.IsNull) return;

            this.m_bIsNull = false;
            this.m_Product *= ParamValue.Value;
        }

        public void Merge(Product other)
        {
            SqlDecimal otherValue = other.Terminate();
            this.Accumulate(otherValue);
        }

        [return: SqlFacet(Precision = 22, Scale = 6)]
        public SqlDecimal Terminate()
        {
            if (m_bIsNull)
            {
                return SqlDecimal.Null;
            }
            else
            {
                return m_Product;
            }
        }

        public void Read(BinaryReader r)
        {
            this.m_bIsNull = r.ReadBoolean();
            this.m_Product = r.ReadDecimal();
        }

        public void Write(BinaryWriter w)
        {
            w.Write(this.m_bIsNull);
            w.Write(this.m_Product);
        }
    }
}

सीएलआर असेंबली स्थापित करें:

-- Turn advanced options on
EXEC sys.sp_configure @configname = 'show advanced options', @configvalue = 1 ;
GO
RECONFIGURE WITH OVERRIDE ;
GO
-- Enable CLR
EXEC sys.sp_configure @configname = 'clr enabled', @configvalue = 1 ;
GO
RECONFIGURE WITH OVERRIDE ;
GO

CREATE ASSEMBLY [RunningProduct]
AUTHORIZATION [dbo]
FROM 'C:\RunningProduct\RunningProduct.dll'
WITH PERMISSION_SET = SAFE;
GO

CREATE AGGREGATE [dbo].[Product](@ParamValue numeric(22,6))
RETURNS numeric(22,6)
EXTERNAL NAME [RunningProduct].[RunningProduct.Product];
GO

यह प्रश्न चल रहे एसयूएम की गणना के बारे में विस्तार से चर्चा करता है और पॉल व्हाइट अपने जवाब में दिखाता है एक सीएलआर फ़ंक्शन कैसे लिखें जो एसयूएम को कुशलतापूर्वक चलाने की गणना करता है। चल रहे उत्पाद की गणना करने वाले फ़ंक्शन को लिखने के लिए यह एक अच्छी शुरुआत होगी।

ध्यान दें, कि वह एक अलग दृष्टिकोण का उपयोग करता है। कस्टम कुल . बनाने के बजाय फ़ंक्शन, पॉल एक फ़ंक्शन बनाता है जो एक तालिका देता है। फ़ंक्शन मूल डेटा को मेमोरी में पढ़ता है और सभी आवश्यक गणना करता है।

अपनी पसंद की प्रोग्रामिंग भाषा का उपयोग करके अपने ग्राहक पक्ष पर इन गणनाओं को लागू करके वांछित प्रभाव प्राप्त करना आसान हो सकता है। बस पूरी तालिका पढ़ें और क्लाइंट पर चल रहे उत्पाद की गणना करें। सीएलआर फ़ंक्शन बनाना समझ में आता है यदि सर्वर पर गणना किया गया चल रहा उत्पाद अधिक जटिल गणनाओं में एक मध्यस्थ कदम है जो डेटा को आगे बढ़ाएगा।

एक और विचार जो दिमाग में आता है।

एक तृतीय-पक्ष .NET गणित पुस्तकालय खोजें जो Log प्रदान करता हो और Exp उच्च परिशुद्धता के साथ कार्य करता है। इन स्केलर . का CLR संस्करण बनाएं कार्य। और फिर EXP + LOG + SUM() Over (Order by) . का इस्तेमाल करें दृष्टिकोण, जहां SUM बिल्ट-इन टी-एसक्यूएल फंक्शन है, जो Over (Order by) को सपोर्ट करता है और Exp और Log कस्टम CLR फ़ंक्शन हैं जो float नहीं लौटाते हैं , लेकिन उच्च परिशुद्धता decimal

ध्यान दें, उच्च परिशुद्धता गणना भी धीमी हो सकती है। और क्वेरी में CLR स्केलर फ़ंक्शंस का उपयोग करने से यह धीमा भी हो सकता है।



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. 24 घंटे से अधिक समय के साथ SQL सर्वर में hh:mm:ss को सेकंड में कैसे बदलें

  2. SQL सर्वर डेटाबेस से सभी डेटा को स्क्रिप्ट करें

  3. एसक्यूएल सर्वर 2008 के साथ अधिसूचना बदलें

  4. प्राथमिक कुंजी के रूप में GUID का उपयोग करने के लिए, विशेष रूप से प्रदर्शन के संबंध में सर्वोत्तम अभ्यास क्या हैं?

  5. जहां कॉमा सीमांकित मान वाले कॉलम में मान