SQL Server'da her cümlenin her sözcüğünün yalnızca ilk harfini büyük yaz


18

Bir SQL sütununda her cümlenin her kelimenin sadece ilk harfini büyük yazmak istiyorum.

Örneğin, cümle:

'Filmleri severim'

o zaman çıktıya ihtiyacım var:

'Filmleri severim'

Sorgu:

declare @a varchar(15) 

set @a = 'qWeRtY kEyBoArD'

select @a as [Normal text],
upper(@a) as [Uppercase text],
lower(@a) as [Lowercase text],
upper(left(@a,1)) + lower(substring(@a,2,len(@a))) as [Capitalize first letter only]

Burada ilk harfi sadece sütunumda üst, alt ve büyük harf yaptım (burada sadece rastgele bir kelime koydum).

İşte sonuçlarım:

resim açıklamasını buraya girin

Bunu yapmak için herhangi bir olasılık var mı?

Kullanıcı tanımlı işlevi kullanmadan sonuç alma imkanı var mı?

Çıktıya ihtiyacım var Qwerty Keyboard


11
Bunu neden sql sunucusu içinde yapmak istiyorsunuz? Sunum katmanınız bunu verimli bir şekilde ele almalıdır!
Kin Shah

SQL Server'a içe aktarılan kötü verileri temizlerken her zaman bir sunum katmanınız yoktur ve bunu yapmak için bir C # programı yazmak istemezsiniz. Evet, bir CLR işlevine yatırım yapabilirsiniz, ancak işe yarayan hızlı ve kirli bir şeye ne dersiniz.
Jeffrey Roughgarden

Yanıtlar:


26
declare @a varchar(30); 

set @a = 'qWeRtY kEyBoArD TEST<>&''"X';

select stuff((
       select ' '+upper(left(T3.V, 1))+lower(stuff(T3.V, 1, 1, ''))
       from (select cast(replace((select @a as '*' for xml path('')), ' ', '<X/>') as xml).query('.')) as T1(X)
         cross apply T1.X.nodes('text()') as T2(X)
         cross apply (select T2.X.value('.', 'varchar(30)')) as T3(V)
       for xml path(''), type
       ).value('text()[1]', 'varchar(30)'), 1, 1, '') as [Capitalize first letter only];

Bu, önce tüm boşlukları boş etiketle değiştirerek dizeyi XML'e dönüştürür <X/>. Daha sonra kullanarak satır başına bir kelime almak için XML'i parçalar nodes(). Satırları bir değere geri döndürmek için for xml pathhile kullanılır.


8
Ve bu kod tam olarak neden SQL'de asla yapmazdım. Cevabın yanlış olduğunu söylememek - bu istendi. Ancak standart SQL, bu tür dize manipülasyonu için gülünç derecede hasta. CLR tabanlı bir işlev çalışır ya da yalnızca sunum katmanında çalışır.
TomTom

8
@TomTom Karmaşık görünüyor, ancak ürettiği sorgu planına kıyasla hiçbir şey yok ve herhangi bir standart tarafından hızlı olmayacak. Ancak , sorguda gerçekte neler olup bittiğini ve neden olduğu gibi yazıldığını öğrenmek eğitici ve eğlencelidir . Sorun bir dize bölme işlevi (sayı tablosu) ile çözülebilir. for xml pathBirleştirme hile önlemek zor . Hız ve verimlilik önemliyse en iyi seçenek olan CLR'ye gitmediğiniz sürece.
Mikael Eriksson

15

SQL Server 2016'da bunu R ile yapabilirsiniz, örn.

-- R capitalisation code stolen from here:
-- http://stackoverflow.com/questions/6364783/capitalize-the-first-letter-of-both-words-in-a-two-word-string

EXEC sp_execute_external_script
    @language = N'R',
    @script = N'
simpleCap <- function(x) {
  s <- strsplit(x, " ")[[1]]
  paste(toupper(substring(s, 1,1)), substring(s, 2),
        sep="", collapse=" ")
}             

OutputDataSet <- as.data.frame((sapply(as.vector(InputDataSet$xtext), simpleCap)))',
    @input_data_1 = N'SELECT LOWER(testString) xtext FROM dbo.testStrings'
WITH RESULT SETS ( ( properCase VARCHAR(50) NOT NULL ) );

Yapmanız gerekip gerekmediği farklı bir sorudur :)


Kesinlikle yapmamalısın. Bazen en az kötü seçenek, ya da OP'nin belirttiği gibi, hızlı ve kirli olmaları gerekiyor.
Jonathan Fite

13

Belki aptallık yapıyorum ama sağlanan bazı sorguya karşı yazdım aşağıdaki sorgu kontrol, bu biraz daha verimli gibi görünüyor (indeksleme bağlı olarak).

Kod biraz aptalca, ama aptal görünüyorsa ama işe yarıyorsa aptal olmadığını söyleyen bir söz yok.

Begin

    Declare @text Varchar(30);

    Set @text = 'qWeRtY kEyBoArD TEST<>&''"X';

    Declare @1 Varchar(2)= ' a'
      , @2 Varchar(2)= ' b'
      , @3 Varchar(2)= ' c'
      , @4 Varchar(2)= ' d'
      , @5 Varchar(2)= ' e'
      , @6 Varchar(2)= ' f'
      , @7 Varchar(2)= ' g'
      , @8 Varchar(2)= ' h'
      , @9 Varchar(2)= ' i'
      , @10 Varchar(2)= ' j'
      , @11 Varchar(2)= ' k'
      , @12 Varchar(2)= ' l'
      , @13 Varchar(2)= ' m'
      , @14 Varchar(2)= ' n'
      , @15 Varchar(2)= ' o'
      , @16 Varchar(2)= ' p'
      , @17 Varchar(2)= ' q'
      , @18 Varchar(2)= ' r'
      , @19 Varchar(2)= ' s'
      , @20 Varchar(2)= ' t'
      , @21 Varchar(2)= ' u'
      , @22 Varchar(2)= ' v'
      , @23 Varchar(2)= ' w'
      , @24 Varchar(2)= ' x'
      , @25 Varchar(2)= ' y'
      , @26 Varchar(2)= ' z';

Set @text=' '+@text

    Select  LTrim(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Lower(@text) ,
                                                              @1 , Upper(@1)) ,
                                                              @2 , Upper(@2)) ,
                                                              @3 , Upper(@3)) ,
                                                              @4 , Upper(@4)) ,
                                                              @5 , Upper(@5)) ,
                                                              @6 , Upper(@6)) ,
                                                              @7 , Upper(@7)) ,
                                                              @8 , Upper(@8)) ,
                                                              @9 , Upper(@9)) ,
                                                              @10 , Upper(@10)) ,
                                                              @11 , Upper(@11)) ,
                                                              @12 , Upper(@12)) ,
                                                              @13 , Upper(@13)) ,
                                                              @14 , Upper(@14)) ,
                                                              @15 , Upper(@15)) ,
                                                              @16 , Upper(@16)) ,
                                                              @17 , Upper(@17)) ,
                                                              @18 , Upper(@18)) ,
                                                              @19 , Upper(@19)) ,
                                                              @20 , Upper(@20)) ,
                                                            @21 , Upper(@21)) ,
                                                    @22 , Upper(@22)) , @23 ,
                                            Upper(@23)) , @24 , Upper(@24)) ,
                            @25 , Upper(@25)) , @26 , Upper(@26)));


end

2
Bu harika ve korkunç bir cevap. Özellikle başlangıçta tarandığınız alanı ve sonunda sıyrılan alanı seviyorum.
BradC

2
@BradC iğrenç, ama bir veri kümesine karşı XML yöntemiyle karşılaştırıldığında denediğimde maliyetinin bir kısmında çalışıyor gibi görünüyor!
Chris J

Hepimizin iyi olduğuna inandığımız şey hakkındaki muazzamlık ve dürüstlük için +1. Muhtemelen bunun hata ayıklamak ve sürdürmek zor olduğunu, ancak çok dar ve asla değişen gereksinimleri olan bir "asla çalışan bir sisteme dokunmayın" olarak, bu sadece bağlam içinde okunması gerektiği için oylanmayı hak ediyor.
kaiser

9

Başka bir seçenek de bunu SQLCLR ile ele almaktır. .NET'te zaten bunu yapan bir yöntem bile vardır: TextInfo.ToTitleCase (in System.Globalization). Bu yöntem, her sözcüğün ilk harfini Büyük Harf ve kalan harfleri Küçük Harfe dönüştürür. Buradaki diğer tekliflerden farklı olarak, tümüyle büyük harf olan sözcükleri de atlayarak kısaltma olduklarını varsayar. Tabii ki, bu davranış istenirse, bunu yapmak için T-SQL önerilerinden herhangi birini güncellemek yeterince kolay olacaktır.

.NET yönteminin bir yararı, Tamamlayıcı Karakterler olan Büyük Harfler yapabilmesidir. Örneğin: DESERET SMALL LETTER OW , DESERET CAPITAL LETTER OW'un büyük harfli eşlemesine sahiptir (her ikisi de buraya yapıştırdığımda her ikisi de kutu olarak görünür ) , ancak UPPER()işlev, küçük harfli sürümü büyük harfle değiştirmez, hatta geçerli Veritabanı için varsayılan Harmanlama olarak ayarlanır Latin1_General_100_CI_AS_SC. Bu, bir Harmanlama: Harmanlama ve Unicode Desteği: Tamamlayıcı Karakterler kullanılırken listelenmeyen MSDN belgelerine UPPERve LOWERişlev şemasına uygun görünüyor ._SC

SELECT N'DESERET SMALL LETTER OW' AS [Label], NCHAR(0xD801)+NCHAR(0xDC35) AS [Thing]
UNION ALL
SELECT N'DESERET CAPITAL LETTER OW' AS [Label], NCHAR(0xD801)+NCHAR(0xDC0D) AS [Thing]
UNION ALL
SELECT N'SmallButShouldBeCapital' AS [Label], UPPER(NCHAR(0xD801)+NCHAR(0xDC35)) AS [Thing]

Döndürür (ek karakteri görebilmeniz için büyütülmüş):

UPPER () işlevinin Tamamlayıcı Karakterle çalışmadığını gösteren sorgu sonucu

Unicode.org'daki aşağıdaki arama özelliğini kullanarak küçük ve büyük harf olan karakterlerin tam (ve geçerli) listesini görebilirsiniz ("DESERET" e ulaşıncaya kadar aşağı doğru kaydırarak Tamamlayıcı Karakterleri görebilirsiniz. bölümüne gidin veya Control-Fo kelimeyi tıklayın ve arayın):

http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AChanges_When_Titlecased%3DYes%3A%5D

Dürüst olmakla birlikte, bu büyük bir fayda değildir, çünkü herkesin başlık kasası olabilecek herhangi bir Ek Karakterden herhangi birini kullandığından şüphelenilir. Her iki durumda da, burada SQLCLR kodu:

using System.Data.SqlTypes;
using System.Globalization;
using Microsoft.SqlServer.Server;

public class TitleCasing
{
    [return: SqlFacet(MaxSize = 4000)]
    [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true)]
    public static SqlString TitleCase([SqlFacet(MaxSize = 4000)] SqlString InputString)
    {
        TextInfo _TxtInf = new CultureInfo(InputString.LCID).TextInfo;
        return new SqlString (_TxtInf.ToTitleCase(InputString.Value));
    }
}

İşte @ MikaelEriksson'un önerisi - NVARCHARverileri işlemek ve tüm büyük harfli kelimeleri (.NET yönteminin davranışıyla daha yakından eşleştirmek için) atlamak için biraz değiştirilmiş - bu T-SQL uygulamasının ve SQLCLR uygulaması:

SET NOCOUNT ON;
DECLARE @a NVARCHAR(50);

SET @a = N'qWeRtY kEyBoArD TEST<>&''"X one&TWO '
         + NCHAR(0xD801)+NCHAR(0xDC28)
         + N'pPLe '
         + NCHAR(0x24D0) -- ⓐ  Circled "a"
         + NCHAR(0xFF24) -- D  Full-width "D"
         + N'D u'
         + NCHAR(0x0308) -- ̈  (combining diaeresis / umlaut)
         + N'vU'
         + NCHAR(0x0308) -- ̈  (combining diaeresis / umlaut)
         + N'lA';
SELECT @a AS [Original];

SELECT STUFF((
       SELECT N' '
              + IIF(UPPER(T3.V) <> T3.V COLLATE Latin1_General_100_BIN2, 
                    UPPER(LEFT(T3.V COLLATE Latin1_General_100_CI_AS_SC, 1))
                    + LOWER(STUFF(T3.V COLLATE Latin1_General_100_CI_AS_SC, 1, 1, N'')),
                    T3.V)
       FROM (SELECT CAST(REPLACE((SELECT @a AS N'*' FOR XML PATH('')), N' ', N'<X/>')
                    AS XML).query('.')) AS T1(X)
       CROSS APPLY T1.X.nodes('text()') AS T2(X)
       CROSS APPLY (SELECT T2.X.value('.', 'NVARCHAR(70)')) AS T3(V)
       FOR XML PATH(''), TYPE
       ).value('text()[1]', 'NVARCHAR(70)') COLLATE Latin1_General_100_CI_AS_SC, 1, 1, N'')
                AS [Capitalize first letter only];

SELECT dbo.TitleCase(@a) AS [ToTitleCase];

SQLCLR üzerinden T-SQL XML kodu ve ToTitleCase çıktısını gösteren sorgu sonucu

Davranıştaki diğer bir fark, bu özel T-SQL uygulamasının sadece boşluklara ayrılmasıdır, oysa ToTitleCase()yöntem harf olmayan çoğu kelime ayırıcısı olarak kabul edilir (dolayısıyla "bir & İKİ" parçanın işlenmesindeki fark).

Her iki uygulama, birleştirme dizilerini doğru şekilde işler. "ÜvÜlA" daki aksanlı harflerin her biri bir temel harf ve birleştirici bir diyaer / umlauttan (her harfin üstünde iki nokta) oluşur ve her iki testte de doğru şekilde diğer duruma dönüştürülür.

Son olarak, SQLCLR sürümünün beklenmedik bir dezavantajı, çeşitli testlerle gelince, .NET kodunda Çember Harfleri (artık Microsoft Connect'te bildirildi - UPDATE: Connect /dev/null- kelimenin tam anlamıyla - taşındı, bu yüzden sorun hala varsa bunu yeniden göndermem gerekebilir). .NET kütüphanesi, Dairesel Harfleri sözcük ayırıcıları olarak görür, bu nedenle ""D" yi "Ⓐdd" ye gerektiği gibi dönüştürmez.


Bilginize

Enkapsüle Önceden yapmaktan SQLCLR fonksiyonu TextInfo.ToTitleCaseyöntemi yukarıda belirtilen Serbest sürümünde artık kullanılabilir SQL # olarak (Yazdığım olan) String_ToTitleCase ve String_ToTitleCase4k .

😺


5

Mikael Eriksson'un cevabına alternatif olarak , çok satırlı seçim ifadelerinde değişken ayarın tescilli T-SQL işlenmesini kullanmayı düşünebilirsiniz.

SQL Server'da, bir değişken SELECT ifadesinin bir parçası olarak ayarlandığında, her satır ayarlı mantığın yinelemesini yürütür.

İnsanlar desteklenmiyor olsa da dizeleri birleştirmek için bu yöntemi kullanırlar ve bazı resmi olarak belgelenmiş sorunlar vardır . Resmi sorun belirli ORDER BY özellikleriyle ilgilidir ve burada buna ihtiyacımız yoktur, bu yüzden belki de güvenli bir seçenektir.

Burada, alfabenin 26 harfini tekrarlıyoruz ve önünde boşluk varsa, büyük harfli bir sürümle değiştiriyoruz. (Dizeyi başlangıçta ilk harfi büyük yazıp geri kalanını küçük harflerle hazırlayarak, sorunuzda yaptığınız gibi hazırlarız.)

SQL biraz karmaşıktır, çünkü yaptığı yerine geçmenin 26 yinelemesini oluşturmak için bir Tally Tablosu - bir sayı tablosu - kullanılmasını gerektirir. Bu sayı tablosunu üretmek için kullanışlı satır içi tablo değerli kullanıcı tanımlı bir işlev (TVF) yapabilirsiniz veya fiziksel bir tablo bile kullanabilirsiniz.

Bu seçeneğin bir dezavantajı, bir değişken ayarlamayı gerektirmesi gerektiği için satır içi TVF'nin bir parçası olamamasıdır. Dolayısıyla, bu yöntemi çıktınızın bir sütununa uygulamak isterseniz, bunu çok ifadeli bir TVF'ye veya skaler kullanıcı tanımlı bir işleve sarmanız gerekir.

Ancak, sorgu planı çok daha basittir ve muhtemelen XML yönteminden önemli ölçüde daha hızlıdır. Anlamanızın daha kolay olduğunu da iddia edebilirsiniz (özellikle kendi hesap tablonuz varsa).

DECLARE
    @a VARCHAR(15) = 'qWeRtY kEyBoArD';

SELECT
    @a = UPPER(LEFT(@a,1)) + LOWER(SUBSTRING(@a,2,LEN(@a)));

WITH TallyTableBase AS
(
    SELECT
        0 AS n
    FROM    (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) AS t(n)
)
SELECT
    @a = REPLACE(@a, ' ' + CHAR(n.n), ' ' + CHAR(n.n))
FROM        (
                SELECT      TOP 26 ROW_NUMBER() OVER (ORDER BY (SELECT 1)) + 64 AS n
                FROM        TallyTableBase a
                CROSS JOIN  TallyTableBase b
            ) AS n;

SELECT
    @a AS [NewValue];

(Bunu çok daha büyük bir dize kullanarak test ettim ve XML çözümü için yaklaşık 6ms vs 14ms idi.)

Bu çözümle ilgili bazı ek sınırlamalar vardır. Yazıldığı gibi, büyük / küçük harfe duyarlı olmayan bir harmanlama olduğunu varsayarsak da, bir harmanlama belirterek veya arama teriminde LCASE'i çalıştırarak bazı performans pahasına bu sorunu ortadan kaldırabilirsiniz . Ayrıca yalnızca standart ASCII harflerini ele alır ve karakter kümesindeki yerleşimlerine dayanır , bu nedenle ñ ile hiçbir şey yapmaz.


3

Yalnızca bir boşluktan sonra kelimeleri büyük harfle yazmak istediğinizi varsayarsak, burada bunu yapmanın başka bir yolu var.

DECLARE @String VARCHAR(1000)
SET @String = 'qWeRtY kEyBoArD tEst'

/*
Set the string to all lower case and
add a space at the beginning to ensure
the first letter gets capitalized
in the CTE
*/
SET @String = LOWER(' ' + @String)  

/*
Use a Tally "Table" as a means of
replacing the letter after the space
with the capitalize version of the
letter
*/
;WITH TallyTable
AS
(
    SELECT TOP 1000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as N
    FROM master.sys.all_columns a CROSS JOIN master.sys.all_columns b

)
SELECT @String = REPLACE(@String,SUBSTRING(@String,CHARINDEX(' ',@String,N), 2),UPPER(SUBSTRING(@String,CHARINDEX(' ',@String,N), 2)))
FROM TallyTable
WHERE CHARINDEX(' ',@String,N) <> 0

--Remove the space added to the beginning of the string earlier
SET @String = RIGHT(@String,LEN(@String) - 1)

1

Kurşun geçirmez olmayabilir, ancak umarım bu konuya yardımcı olur.

DECLARE @t VARCHAR(50) = 'the quick brown fox jumps over the lazy dog', @i INT = 0

DECLARE @chk VARCHAR(1)

WHILE @i <= LEN(@t)
BEGIN
    SELECT @chk=SUBSTRING(@t,@i,1)
        IF @chk = CHAR(32)
        BEGIN
            SET @t = STUFF(@t,@i+1,1,UPPER(SUBSTRING(@t,@i+1,1)))
        END
    SET @i=@i+1
END
PRINT @t

0

Bunu yapmak için bir Firebird veritabanında kullandığım prosedür aşağıdadır. Muhtemelen çok temizlenebilir ama benim için işi halletti.

set term ~;

Create Procedure EachWordCap

As

Declare Variable lcaption varchar(33);
Declare Variable lcurrentpos integer;
Declare Variable lstringlen integer;
begin
    for select ' ' || trim(lower(imagedata.imagename)) from imagedata
    where imagedata.imagename is not null and imagedata.imagename != ''
    into :lcaption
    do 
    begin
        lcurrentpos = 0;
        lstringlen = char_length(lcaption);
        while (lcurrentpos != 1) do
        begin
            lcurrentpos = position(' ', lcaption, iif(lcurrentpos = 0, 1,lcurrentpos)) + 1 ;
            lcaption = left(lcaption,lcurrentpos - 1) || upper(substring(lcaption from lcurrentpos for 1)) || right(lcaption,lstringlen - lcurrentpos);
        end
        --Put what you want to do with the text in here
    end
end~
set term ;~

0

Yinelemeli CTE'ler bu tür şeyler için oldukça iyidir.

Muhtemelen büyük işlemler için özellikle verimli değildir, ancak saf bir SQL select deyiminde bu tür işlemlere izin verir:

declare @a varchar(100) 

set @a = 'tHe qUiCk bRoWn FOX jumps   OvEr The lAZy dOG';

WITH [CTE] AS (
  SELECT CAST(upper(Left(@a,1)) + lower(substring(@a,2,len(@a))) AS VARCHAR(100)) AS TEXT,
         CHARINDEX(' ',@a) AS NEXT_SPACE
  UNION ALL
  SELECT CAST(Left(TEXT,NEXT_SPACE) + upper(SubString(TEXT,NEXT_SPACE+1,1)) + SubString(TEXT,NEXT_SPACE+2,1000) AS VARCHAR(100)),
         CHARINDEX(' ',TEXT, NEXT_SPACE+1)
  FROM [CTE]
  WHERE NEXT_SPACE <> 0
)

SELECT TEXT
FROM [CTE]
WHERE NEXT_SPACE = 0

Çıktı:

The Quick Brown Fox Jumps   Over The Lazy Dog

0

Bu sürümü beğendim. Basit ve bir işlev oluşturmak için kullanılabilir, sadece doğru SQL Server sürümüne sahip olmanız gerekir:

WITH words
AS (
    SELECT upper(left(Value, 1)) + lower(substring(Value, 2, len(Value))) AS word
    FROM STRING_SPLIT('Lorem ipsum dolor sit amet.', ' ')
    )
SELECT STRING_AGG(words.word, ' ')
FROM words

Hangisi doğru sürüm?
dezso

SQL Server (2016 ile başlayan)
Cristi

-2
DECLARE @someString NVARCHAR(MAX) = 'In this WHILE LOOP example' 

DECLARE @result NVARCHAR(MAX) =Upper(SUBSTRING(@someString, 1, 1))

DECLARE @index INT =2 

WHILE LEN(@someString)>@index

BEGIN

SET @result= @result+CASE WHEN CHARINDEX(' ',@someString,@index)<>0 THEN LOWER(SUBSTRING(@someString, @index, CHARINDEX(' ',@someString,@index)-@index+1)) +Upper(SUBSTRING(@someString, CHARINDEX(' ',@someString,@index)+1, 1)) ELSE  LOWER(SUBSTRING(@someString,@index, LEN(@someString) )) END

SET @index=CASE WHEN CHARINDEX(' ',@someString,@index)<>0 THEN CHARINDEX(' ',@someString,@index)+2 ELSE  LEN(@someString)+1  END

 END

SELECT  @result 

Umarım yardımcı olur ...


Veritabanı Yöneticilerine Hoş Geldiniz! Lütfen sorgunuzun yazarın sorununu nasıl çözdüğünü açıklayın; açıklama yapmadan cevaplar genellikle iyi karşılanmaz.
Glorfindel

-3

Test verisi

declare @word varchar(100)
with good as (select 'good' as a union select 'nice' union select 'fine')
select @word = (SELECT TOP 1 a FROM good ORDER BY NEWID())

uygulama

select substring(Upper(@word),1,1) + substring(@word, 2, LEN(@word))

Zaten ayrı olan kelimeleri büyük harfle yazmak kolaydır. OP'nin bir dize içindeki sözcükleri nasıl tanımlayacağına ve her birini nasıl aktifleştireceğine inanıyorum.
Tüm
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.