PDA

View Full Version : حذف کاراکتر های تکراری متوالی از یک رشته



محمد سلیم آبادی
دوشنبه 27 اردیبهشت 1389, 02:41 صبح
داشتم یک مقاله از سایت sqlservercentral (http://www.sqlservercentral.com/articles/Advanced+Querying/2547)را راجب sequence numbers table می خواندم که به بخشی با عنوان "حذف کاراکترهای side-by-side تکراری با کمک جدول کمکی اعداد متوالی از یک تا N" رسیدم.

نوسینده از روش جالبی استفاده نکرده بود. در ادامه روش خودم را معرفی می کنم که برای هر سناریویی مورد استفاده می تواند قرار گیرد. و بسیار خوانا و ساده است. (البته با کمک گرفتن از جدول اعداد)

صورت مساله
داده ی زیر را تصور کنید


befor
-------------------------------------------------------------
MMMMoooohhhhaaaaammmaaaaaaaaad Salimmmmmmabadi

ما نیاز داریم کاراکترهای تکراری کنار هم را حذف کنیم. در نتیجه رشته ی مورد نظر ما این خواهد بود:


removed
----------------------
Mohamad Salimabadi



راه حل جدید:



--Declaration string variables
DECLARE @s VARCHAR(500) = 'MMMMoooohhhhaaaaammmaaaaaaaaad Salimmmmmmabadi'
DECLARE @result VARCHAR(500)='';

--Publishing auxiliary sequence numbers table with native approach
WITH c AS
(SELECT 1 AS n
UNION ALL
SELECT 1 + n FROM c WHERE n < 100),
c1 AS (SELECT n = ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM c c1
CROSS JOIN c c2)

--Splitting the string with numbs table
, k AS
(SELECT n, k = SUBSTRING (@s, n, 1)
FROM c1
WHERE n <= LEN(@s))

--Filtering characters and then concatenating them
SELECT @result = @result + k
FROM k k1
WHERE NOT EXISTS
(SELECT *
FROM k k2
WHERE k1.k = k2.k
AND k1.n+1 = k2.n);


SELECT @result AS removed;

fakhravari
یک شنبه 03 دی 1391, 01:06 صبح
با سلام
چطوری میتونم این کد را روی یک جدول پیاده کنم.
مثلا از جدول a روی رکورد هایی که این شرط را دارن پیاده بشه

محمد سلیم آبادی
یک شنبه 03 دی 1391, 09:07 صبح
چطوری میتونم این کد را روی یک جدول پیاده کنم.شما میتونید یک تابع مخصوص اینکار ایجاد کنید و بعد در کوئری خیلی راحت هر موقع که نیاز داشتین استفاده کنید برای ساخت تابع به لینک زیر مراجعه کنید:
http://www.30sharp.com/article/13/227/11/%D8%AD%D8%B0%D9%81-%DA%A9%D8%A7%D8%B1%D8%A7%DA%A9%D8%AA%D8%B1%D9%87%D 8%A7%DB%8C-%D8%AA%DA%A9%D8%B1%D8%A7%D8%B1%DB%8C-%D8%AF%D8%B1-%DA%A9%D9%86%D8%A7%D8%B1-%D9%87%D9%85%D8%AF%DB%8C%DA%AF%D8%B1.aspx

اگر هم نمیخواهین تابع تعریف کنید. میتونید همه رو در قالب یک کوئری بیارین.
فرض کنید نام جدولتون your_table هست و کلید جدول Id و ستونی که مقدار رشته ای داره و میخواهین اصلاح بشه هست your_column آنگاه داریم:

with c1 as
(
select *
from your_table t
cross apply (SELECT n , SUBSTRING (your_column, n, 1)
FROM number_table
WHERE n <= LEN(your_column)) c(n, k)
),
c2 as
(
select c1.*
from c1 left join c1 as c2
on c1.id = c2.id
and c1.n = c2.n-1
where c1.k <> c2.k or c2.id is null
)
select t.*, replace(d.list,'~@','') as no_replicate from your_table t
cross apply (select '~@'+k
from c2
where c2.id = t.id
order by n
for xml path('')) d(list);

fakhravari
یک شنبه 03 دی 1391, 21:20 عصر
ممنون داش msalim (http://barnamenevis.org/member.php?108959-msalim)
والا کمی پیچیده بود.
یه مثال ساده تر در حد پیاده روی این select
SELECT [TopicID],[Message] FROM [B_Topics]

محمد سلیم آبادی
یک شنبه 03 دی 1391, 23:31 عصر
گل پسر،
خب مثالی که آورده بودم هم دقیقا بر اساس همچین جدولی بود شما کافیه به جای topicid و message و b_topics به ترتیب id و your_column و your_table رو قرار بدین. نکته! از قبل شما باید یک جدول اعداد داشته باشین با کد زیر اونو تولید و مقدار دهی کنید:

create table number_table(n int not null primary key)
insert into number_table(n) select row_number() over(Order by (select 1)) as n from sys.all_columns

یا یه کار دیگه، بعد از ساخت تابع زیر کوئریتون رو به این شکل بنوسید:

select topicid, dbo.fnremovedupesi(message) from b_topics

CREATE FUNCTION dbo.fnRemoveDupesI (@String VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE @result VARCHAR(8000) = '';

SELECT @result = @result + Data
FROM (SELECT ID,
Data,
ROW_NUMBER() OVER (PARTITION BY Data ORDER BY ID) - ID
FROM (SELECT SUBSTRING(@String, n, 1), n
FROM Nums
WHERE n <= LEN(@String)
) D(data, ID)
) D(ID, Data, RowNum)
GROUP BY Data, RowNum
ORDER BY MIN(ID)

RETURN @result
END;

یوسف زالی
دوشنبه 04 دی 1391, 00:04 صبح
سلام.
فکر می کنم به اشتباه خیلی سختش کردید. من این کد رو نوشتم.
بررسی بفرمایید نظرتون رو بگید:


declare @X varchar(100) = 'YYYYOOOOUUUSSSEEEEFFFFFFFFFFF ZAAALLLLLLIIII'
declare @Z varchar(100) = ''
declare @C char

set @X = REPLACE(@X, ' ', '$');

with CTE as
(
select 1 as N
union all
select N +1
from CTE
where N < LEN(@X)
)
select @C = SUBSTRING(@X, 1, 1), @Z += @C, @X = REPLACE(LTRIM(REPLACE(@X, @C, ' ')), ' ', @C)
from CTE

set @X = REPLACE(@Z, '$', ' ')
select @X

محمد سلیم آبادی
دوشنبه 04 دی 1391, 04:01 صبح
سلام،
جالب، الگوریتم زیرکانه ای داره. برای حذف کاراکترهای تکراری متوالی که در اول رشته موجود هستند از ltrim کمک گرفته شده.
کد زیر در واقع عین الگوریتم شماست ولی با یک شیوه پیاده سازی متفاوت، در این روش ما دیگه درگیر کاراکتر های space نمیشیم. و رشته رو به یک طریق دیگه برش میدیم.

declare @X varchar(100) = 'YYYYOOOOUUUSSSEEEEFFFFFFFFFFF ZAAALLLLLLIIII',
@Z varchar(100) = '',
@C char,
@P int;

select @C = SUBSTRING(@X, 1, 1),
@Z += @C,
@P = patindex('%[^'+@C+']%',@X),
@X = case when @P > 0 then substring(@X,@p,100) else '' end
from (select top(len(@x))1 n from sys.columns)d

set @X = @Z
select @X

سوء تفاهم نشه، هدف حل مساله توسط کوئری هست (یعنی سر و تهش رو در یک کوئری هم بیاریم) بدون درگیر شدن با بحث انتسابات در select (مثل کد شما) و یا تکنیک های دیگه مثل حلقه.
کوئری هایی که در اولین و دومین پستم مطرح کردم این قابلیت رو دارن که روی مقادیر یکایک سطرهای جدول اعمال بشن یعنی به ازای هر سطری، در cross apply ما مقدار رو به فرمت مناسب در میاریم. ولی در روش شما این امکان وجود نداره که کد داخل یک کوئری بکار بره (نمیشه در یک کوئری هم زمان انتساب و برگرداندن مقادیر داشت)، شما مجبورین که یک تابع تعریف کنید و در بدنه تابع از کدتون استفاده کنید.

tooraj_azizi_1035
دوشنبه 04 دی 1391, 10:33 صبح
غیر از شهادت راه دیگه ای هم هست.
سختی کار رو در SQL Server میشه با CLR Integration حل کرد. یعنی یک User Defined Function در C#‎ نوشت و در SQL Server استفاده کرد.

public static string RemoveDuplicates(string input)
{
return new string(input.ToCharArray().Distinct().ToArray());
}

یوسف زالی
دوشنبه 04 دی 1391, 11:05 صبح
جناب tooraj:
اگر بنا به CLR نوشتن بود که ..!
بی خیال!

جناب سلیم،
خدا رو شکر، نگران بودم برای شما سوء تفاهم نشه. خوشحالم که حرفه ای برخورد می کنید.
در مورد توابع حق با شماست. باید بگم که خیلی وقت ها لازم می شه از پیچیدگی کاسته بشه و به خوانایی اضافه بشه.
فکر می کنم تفاوت اصلی میان دو کد مطرح شده بیشتر خوانایی و توانایی اصلاحات و گسترش باشه.
بهتره که این کار در یک تابع باشه.
اما از نظر سرعت ممکنه استفاده مستقیم از کد شما در ردیف های خیلی خیلی زیاد، بهتر باشه. به دلیل ارجاعات متعدد به تابع. اما در واقع اکثر کارهای آدم با زیر 10,000 ردیف (top) راه می افته.
اون کد رو با کمی اصلاح هم می شه تبدیل به کدی کرد که مستقیم کار کنه و احتمالا بسیار شبیه نتیجه کار شما بشه.
در کل خوندن و تحلیل اسکریپت های شما رو دوست دارم. نکات "ابتکاری" (تاکید) شما رو دوست دارم.
موفق باشید.

fakhravari
جمعه 15 دی 1391, 18:22 عصر
با سلام
ALTER FUNCTION [dbo].[DelTextDplicate](@Text nvarchar)
RETURNS NVARCHAR
AS
BEGIN

declare @X nvarchar(100) = @Text;
declare @Z nvarchar(100) = '';
declare @C char;
declare @P int;

select @C = SUBSTRING(@X, 1, 1),
@Z += @C,
@P = patindex('%[^'+@C+']%',@X),
@X = case when @P > 0 then substring(@X,@p,100) else '' end
from (select top(len(@x))1 n from sys.columns)d

set @X = @Z

RETURN @X
END
و بر روی select
SELECT [TopicID]
,[ForumID]
,dbo.DelTextDplicate([Subject])as Subject
FROM [B_Topics]
نتیجه در عکس ببنید.
فکر کنم مشک در طول رشته است؟:متفکر:

fakhravari
جمعه 15 دی 1391, 21:19 عصر
من به NVARCHAR(max) تغیر دادم به شکل زیر شد.
فکر کنم روی رکورد فارسی کاربرد نداره چون جمله بهم میزنه

محمد سلیم آبادی
دوشنبه 18 دی 1391, 09:56 صبح
من به NVARCHAR(max) تغیر دادم به شکل زیر شد.
فکر کنم روی رکورد فارسی کاربرد نداره چون جمله بهم میزنه
حقیقتش من روی متون فارسی راه حل رو بررسی نکردم. نتیجتا کد برای جملات فارسی ضمانتی نداره.