SQL而不是C#中的字符串函数[关闭]

Posted

技术标签:

【中文标题】SQL而不是C#中的字符串函数[关闭]【英文标题】:String Functions in SQL instead of C# [closed] 【发布时间】:2017-04-02 14:55:44 【问题描述】:

我有 2000000 条包含客户姓名的 sql 记录。我想改变如下。

样本数据:

Name
अमरीनaa मोयोद्दिनa शेखa
रऊफa वाहेदaa शेखa 
शहेदाबेगमaa रऊफaa शेखa
इम्रानa रउफ़aa शेखa 
दत्तुaa
कैलास धुमाळ
विलास दत्तु धुमाळ 
बिस्मिल्ला बी अ.हमीद खॉन
इस्माईल खॉन अ.हमीद खॉन 
नसरीन बेगम इस्माईल खान 
अ.हमिद खॉन इमाम खॉन
अजमेर खॉ इमाम खॉ
सुग्रा बी अजमेर खॉ
हनीफाबी अजमेर 
गौस अजमेर खान 
यासीन अजमेर खान 
राबिया बी मुश्ताक अली शेख
मो.सिंकंदर अली अन्वरअली शेख 
गफार मोयीन शेख 
नंदाबाई अशोक 
सचिन आशोक दिवेकर
सोनाजी नामदेव बोराडे
व्दारका राजू गायकवाड 
लिलाबाई सोनाजी बोराडे 
शारदाबाई राजू जगदाळे
अनिता अर्जुन जगदाळे
मंदा सुनील वाढेकर 
विठ्ठल दगडू 
सुनिल विश्वनाथ वाढेकर
शिवाजी विश्वनाथ
गयाबाई शिवाजी 
बाळू विश्वनाथ
वैशाली बाळू वाढेकर 
पांडुरंग नामदेब वाघ नामदेव 
हिराबाई पांडुरंग बाघ पांडुरंग 
सवीता संतोष किर्तीकर
चंद्रकला प्रल्हाद
संतोष प्रल्हाद
अनिल प्रल्हाद
विजय प्रल्हाद किर्तीकर 
राजेंद्र काशिनाथ 
हिराबाई राजेंद्र 
सुरेश पैठणे
नुतन सुरेश 
गौतम पैठणे
शारदा गौतम पैठणे गौतम
राजू अंबादास 
शोभाबाई राजू
सुनिता गोटीराम गायकवाड
बाळकृष्ण भानुदास दुलग

上面的数据有名字,中间名和姓氏,我想要:

    在名称的每个单词中,aa 应替换为名称中任何位置的单个 a(अमरीनaa 将是 अमरीन 和 रऊफaa 将是 रऊफ) 在名称的每个单词中,应删除最后一个单词中的 a(शेखa 将是 शेख,मोयोद्दिनa 将是 मोयोद्दिन) 如果名称有超过 2 个单词,那么最后一个单词将在第一个单词 (विलास दत्तु धुमाळ 将是 धुमाळ विलास दत्तु) 如果名称少于 3 个单词,则它与 कैलास धुमाळ 将 कैलास धुमाळ 相同,而 दत्तुaa 将是 दतli्तुaa) 应删除所有以空格开头和结尾的单词。

对于上述要求,我使用 C# 硬代码来完全填充,但需要 5 到 8 小时才能完成,我希望这应该在 sql 端完成。

这里是我的 C# 代码:

 int _pcount = 0;
string _qr = "";
string _Name = "";
string _FinalName = "";
string _FNAME = "";
string _LastName = "";
string _MiddleName = "";
string _ID = "";
string[] _Split;
List<string> _a = new List<string>();
DataRowCollection _dr = _CDatabase._MGetDataRows("SELECT _ID, _FULLNAME FROM MYTABLE ORDER BY _FULLNAME"); // This is a function will execute a sql and return DataRowCollection
progressBar1.Maximum = _dr.Count + 1;
progressBar1.Value = 0;
using (SqlConnection con = new SqlConnection("MyConStr"))

    con.Open();
    using (SqlTransaction trans = con.BeginTransaction(IsolationLevel.ReadCommitted))
    
        try
        
            foreach (DataRow _row in _dr)
            
                progressBar1.Value++;
                _pcount++;
                if (_pcount >= 100)
                
                    _pcount = 0;
                    Application.DoEvents();
                
                _ID = _CConvert._MConvertToString(_row[0]);
                _Name = _CConvert._MConvertToString(_row[1]);

                _Split = _Name.Split(' ');
                _a = _Split.ToList();
                _a.Remove(" ");
                _a.Remove(" ");
                _a.Remove(" ");
                _a.Remove(" ");
                _a.Remove(" ");
                _a.Remove(" ");
                switch (_a.Count)
                
                    case 0:
                        
                            _FNAME = _FinalName = _Name;
                            _FNAME = _Name;
                            _LastName = "";
                            _MiddleName = "";
                            break;
                        
                    case 1:
                        
                            _FNAME = _FinalName = _Name;
                            _FNAME = "";
                            _LastName = _Name;
                            _MiddleName = "";
                            break;
                        
                    case 2:
                        
                            _FNAME = _FinalName = _Name;
                            _FNAME = _a[0];
                            _LastName = _FNAME;
                            _MiddleName = _a[1];
                            break;
                        

                    case 3:
                        
                            _FinalName = _a[2] + " " + _a[0] + " " + _a[1];
                            _FNAME = _a[0];
                            _MiddleName = _a[1];
                            _LastName = _a[2];
                            break;
                        
                    case 4:
                         // nasreen begum ismail khan
                            _FinalName = _a[3] + " " + _a[0] + " " + _a[1] + " " + _a[2];
                            _FNAME = _a[0] + " " + _a[1]; // nasreen begum
                            _MiddleName = _a[2];// ismail
                            _LastName = _a[3];//khan
                            break;
                        

                    case 5:
                         // jaibunnisa begum gulam dastagir sahab syed
                            _FinalName = _a[4] + " " + _a[0] + " " + _a[1] + " " + _a[2] + " " + _a[3];
                            _FNAME = _a[0] + " " + _a[1]; // jaibunnisa begum
                            _MiddleName = _a[2] + " " + _a[3];// gulam gastagir
                            _LastName = _a[4];//syed
                            break;
                        

                    case 6:
                         // jaibunnisa begum gulam dastagir syed
                            _FinalName = _a[5] + " " + _a[0] + " " + _a[1] + " " + _a[2] + " " + _a[3] + " " + _a[4];
                            _FNAME = _a[0] + " " + _a[1]; // jaibunnisa begum
                            _MiddleName = _a[2] + " " + _a[3] + " " + _a[4];// gulam gastagir
                            _LastName = _a[5];//syed
                            break;
                        
                    case 7:
                         // jaibunnisa begum gulam dastagir syed
                            _FinalName = _a[6] + " " + _a[0] + " " + _a[1] + " " + _a[2] + " " + _a[3] + " " + _a[4] + " " + _a[5];
                            _FNAME = _a[0] + " " + _a[1]; // jaibunnisa begum
                            _MiddleName = _a[2] + " " + _a[3] + " " + _a[4] + " " + _a[5];// gulam gastagir
                            _LastName = _a[6];//syed
                            break;
                        
                    default:
                        
                            _FinalName = _Name;
                            _FNAME = "";
                            _LastName = "";
                            _MiddleName = "";
                            break;
                        
                

                _qr = "UPDATE MYTABLE SET _FULLNAME = N'" + _FinalName + "' WHERE _ID = '" + _ID + "'";
                _mExcute(_qr, con, trans);
            
            trans.Commit();
            con.Close();
            trans.Dispose();
            MessageBox.Show("DONE");
        
        catch (Exception ex)
        
            trans.Rollback();
            con.Close();
            _CShowMessageBox._MShowErrorMessageBox(ex.Message);
        
    

【问题讨论】:

“但它需要 5 到 8 小时才能完成,我希望这应该在 sql 端完成。” - 听起来你希望它快,并假设它在 SQL 中会更快。那么,与慢速 SQL 解决方案相比,您更喜欢快速的 C# 解决方案,还是它实际上需要是 SQL? 您的代码中的某些内容没有意义。这行 Name.Trim().Replace(" ", ""); 应该删除空格,但它根本不起作用,因为您需要将 Replace 的返回值重新评估为 _Name多变的。然后你尝试分割空格(你应该在之前的行中删除,最后 Remove 调用由于替换的相同原因不做任何事情。也许你需要在搜索如何变得更好之前调试一下这段代码表演。 @GolezTrol SQL 将比 c# 快,因为在 c# 中我正在使用 foreach 循环,这需要时间。以及最后我正在更新一条记录,所以这也是花时间的另一个原因。 你分析了吗?我会添加一些计数器或只是一些 Stopwatch 实例,并找出它慢的地方,然后根据它进行优化。您也可以使用并行 foreach 来多线程,而不是按顺序执行每条记录。最后使用 sql 参数而不是字符串连接,如果有人在他们的名字中有 ',这将防止可能的错误。在项目编号 1,999,999 上出现 sql 错误并让事务回滚真的很糟糕。 此外,如果您在从表中读取名称时使用 DataReader 而不是一次将所有内容读入内存,效率会更高。使用 2 个连接,一个用于阅读器,一个用于更新命令。在提交事务之前,请务必关闭阅读器和相应的连接。 【参考方案1】:

我不清楚您的字符串处理是否可以轻松地在 SQL 中实现,但为了更快,它必须允许基于集合的方法。比如:

UPDATE MYTABLE SET _FULLNAME = dbo.getFullName(paramters)

您的代码运行缓慢的主要原因是大量的 UPDATE 语句。如果您使用 SQL Profiler,您将看到您生成了多少活动。

在尝试转换为 SQL 之前,我会尝试以下操作:

1) 将您的全名预先计算为字符串列表 2)创建一个结构如下的缓冲表(Buffer):

ID INT NOT NULL,
FullName NVARCHAR(255) NOT NULL

3) 使用Bulk Insert 持久化到 Buffer。 Bulk insert 更快(几十倍,甚至几百倍),因为它最大限度地减少了您的应用程序和 SQL Server 之间的往返行程。

4) 使用语句从缓冲区更新到最终表

UPDATE Dest
SET T.FullName = B.FullName
FROM MYTABLE T
JOIN Buffer B ON B.ID = T.ID

【讨论】:

【参考方案2】:

首先创建两个函数:

-- Splits the name parts
CREATE FUNCTION [dbo].[Split]
(
    @String NVARCHAR(4000),
    @Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
    WITH Split(stpos,endpos)
    AS(
        SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos
        UNION ALL
        SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1)
            FROM Split
            WHERE endpos > 0
    )
    SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
        'Data' = SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos)
    FROM Split
)

-- Formats the name
CREATE FUNCTION [dbo].[FormatName]
( 
    @Name NVARCHAR(200)
)
RETURNS NVARCHAR(200)
AS
BEGIN
    DECLARE @CountNameParts AS INT
    DECLARE @FirstName AS NVARCHAR(200)
    DECLARE @MiddleName AS NVARCHAR(200)
    DECLARE @LastName AS NVARCHAR(200)

    SET @CountNameParts = 0
    SET @FirstName = ''
    SET @MiddleName = ''
    SET @LastName = ''

    DECLARE @UserName TABLE(
        Id INT NOT NULL,
        DATA NVARCHAR(200) NOT NULL
    )

    --5.All Words staring and ending blank spaces should be removed.
    INSERT INTO @UserName
    SELECT ID, Data
    from dbo.Split(LTRIM(RTRIM(@Name)), ' ')

    SELECT @CountNameParts=count(*) from @UserName
    IF @CountNameParts = 3
    BEGIN
        SELECT @LastName=Data from @UserName where Id = 3
        SELECT @MiddleName=Data from @UserName where Id = 2
        SELECT @FirstName=Data from @UserName where Id = 1
    END
    ELSE IF @CountNameParts = 2
    BEGIN
        SELECT @LastName=Data from @UserName where Id = 2
        SELECT @FirstName=Data from @UserName where Id = 1
    END
    ELSE
    BEGIN
        SELECT @FirstName=Data from @UserName where Id = 1
    END

    --2.In every words of names a should be removed which is in in last in word ( शेखa will be शेख and मोयोद्दिनa will be मोयोद्दिन)
    SELECT @LastName=REPLACE(@LastName, 'a', '')

    --1.In every words of names aa should be replace with single a which is any where in name ( अमरीनaa will be अमरीन and रऊफaa will be रऊफ)
    SELECT @FirstName=REPLACE(@FirstName, 'aa', 'a')
    SELECT @MiddleName=REPLACE(@MiddleName, 'aa', 'a')
    SELECT @LastName=REPLACE(@LastName, 'aa', 'a')

    --3.If names have more then 2 words then last word will be in fisrt (विलास दत्तु धुमाळ will be धुमाळ विलास दत्तु)
    IF @CountNameParts = 2
    BEGIN
        SET @MiddleName=@FirstName
        SET @FirstName=@LastName
        SET @LastName=@MiddleName
        SET @MiddleName=''
    END

    --4.If names have less then 3 words then it will be same as it is कैलास धुमाळ will कैलास धुमाळ and दत्तुaa will be दत्तुaa)

    RETURN @FirstName + ' ' + @MiddleName + ' ' + @LastName
END

然后像这样进行大规模更新:

UPDATE MYTABLE SET _FULLNAME = [dbo].[FormatName](_FULLNAME)

【讨论】:

SELECT @LastName=REPLACE(@LastName, 'a', '') 将从@LastName 中删除所有a,只需要删除最后一个单词(Imrana Alia Khana 将是 Imran Ali Khan ) 我已经提供了基本功能。您可以根据需要编辑业务规则...【参考方案3】:

您可以用 C# 语言编写 SQL CLR 函数。 您可以在 SQL 语句中使用此功能,一次更新完成任务!

[SqlFunction()]
public static SqlDouble addTax(SqlDouble originalAmount)

    < here is your code in C# (you already done this part!)>

文章如何部署这个: http://www.c-sharpcorner.com/UploadFile/dacca2/sql-clr-for-beginner-part-3-create-function-in-sql-clr/

【讨论】:

以上是关于SQL而不是C#中的字符串函数[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

对于 C#,在调用 Win32 函数(如 GetWindowText)时使用“字符串”而不是“字符串生成器”是不是有不利之处?

如何减去而不是添加像 sql 函数一样的 Sum() [关闭]

判断包含字符串,有没有类似sql中的 in的函数

SQL CLR 用户定义函数 (C#) 在返回的字符串中的每个现有字符之间添加空字符 (\0)

使用正则表达式从 C# 中的 SQL 语句中查找 SQL 函数

从 SQL Server 获取所有值 [关闭]