ค้นหาข้อความในกระบวนงานที่เก็บไว้ใน SQL Server


820

ฉันต้องการค้นหาข้อความจากขั้นตอนการจัดเก็บฐานข้อมูลทั้งหมดของฉัน ฉันใช้ SQL ด้านล่าง:

SELECT DISTINCT
       o.name AS Object_Name,
       o.type_desc
  FROM sys.sql_modules m
       INNER JOIN
       sys.objects o
         ON m.object_id = o.object_id
 WHERE m.definition Like '%[ABD]%';

ฉันต้องการค้นหา[ABD]ในกระบวนงานที่เก็บไว้ทั้งหมดรวมถึงวงเล็บเหลี่ยม แต่ไม่ได้ให้ผลลัพธ์ที่เหมาะสม ฉันจะเปลี่ยนคำถามเพื่อให้บรรลุสิ่งนี้ได้อย่างไร

คำตอบ:


570

หลบหนีวงเล็บเหลี่ยม:

...
WHERE m.definition Like '%\[ABD\]%' ESCAPE '\'

จากนั้นวงเล็บเหลี่ยมจะถือว่าเป็นตัวอักษรสตริงไม่ใช่ไพ่เสริม


doen't ให้ชื่อสคี :(
Imad

4
@Imad คุณสามารถรับชื่อสคีมาได้โดยเพิ่มSCHEMA_NAME(o.schema_id) AS Schema_Nameไปที่ส่วนคำสั่งที่เลือก
Patricus

6
วิธีนี้ใช้ไม่ได้กับขั้นตอนการจัดเก็บที่ยาวนานขึ้น ใช้ SQL ของ Ullas ด้านล่างแทน
Charles Graham

3
ฉันค้นพบวิธีที่ยากที่สุดที่คำตอบนี้จะค้นหาข้อความขั้นตอนการจัดเก็บ 4000 ตัวแรกเท่านั้น ลองดูที่ลิงค์นี้สำหรับคำตอบที่แท้จริง sqlhints.com/2011/10/01/…
ฮันเตอร์เนลสัน

เช่นเดียวกันที่นี่ information_schema ไม่ได้ค้นหาเนื้อหาข้อความ (ภายใน SProcs) แทนที่จะใช้ "sys.procedures" แทนฉัน ฉันเข้าใจว่า Microsoft แนะนำให้ใช้ data_schema แต่ดูเหมือนว่านี่จะไม่ทำงานสำหรับหลาย ๆ คนดังนั้น Microsoft ควรพิจารณาปัญหานี้
Eddie Kumar


56

คุณลองใช้เครื่องมือของบุคคลที่สามเพื่อทำการค้นหาหรือไม่? มีหลายอย่างให้ใช้ฟรีที่นั่นและช่วยฉันทีหนึ่งครั้งในอดีต

ด้านล่างนี้เป็นสอง Adds SSMS ฉันใช้กับความสำเร็จที่ดี

ApexSQL Search - ค้นหาทั้ง schema และข้อมูลในฐานข้อมูลและมีคุณสมบัติเพิ่มเติมเช่นการติดตามการพึ่งพาและอื่น ๆ ...

SSMS Tools pack - มีฟังก์ชั่นการค้นหาเหมือนกับฟังก์ชั่นเจ๋ง ๆ ก่อนหน้า ไม่ฟรีสำหรับ SQL Server 2012 แต่ก็ยังไม่แพงมาก

ฉันรู้ว่าคำตอบนี้ไม่เกี่ยวข้องกับคำถาม 100% (ซึ่งเฉพาะเจาะจงมากขึ้น) แต่หวังว่าผู้อื่นจะพบว่ามีประโยชน์



31

แนวปฏิบัติที่ดีในการทำงานกับ SQL Server

สร้างขั้นตอนการจัดเก็บด้านล่างและตั้งค่าปุ่มลัด

CREATE PROCEDURE [dbo].[Searchinall]       
(@strFind AS VARCHAR(MAX))
AS
BEGIN
    SET NOCOUNT ON; 
    --TO FIND STRING IN ALL PROCEDURES        
    BEGIN
        SELECT OBJECT_NAME(OBJECT_ID) SP_Name
              ,OBJECT_DEFINITION(OBJECT_ID) SP_Definition
        FROM   sys.procedures
        WHERE  OBJECT_DEFINITION(OBJECT_ID) LIKE '%'+@strFind+'%'
    END 

    --TO FIND STRING IN ALL VIEWS        
    BEGIN
        SELECT OBJECT_NAME(OBJECT_ID) View_Name
              ,OBJECT_DEFINITION(OBJECT_ID) View_Definition
        FROM   sys.views
        WHERE  OBJECT_DEFINITION(OBJECT_ID) LIKE '%'+@strFind+'%'
    END 

    --TO FIND STRING IN ALL FUNCTION        
    BEGIN
        SELECT ROUTINE_NAME           Function_Name
              ,ROUTINE_DEFINITION     Function_definition
        FROM   INFORMATION_SCHEMA.ROUTINES
        WHERE  ROUTINE_DEFINITION LIKE '%'+@strFind+'%'
               AND ROUTINE_TYPE = 'FUNCTION'
        ORDER BY
               ROUTINE_NAME
    END

    --TO FIND STRING IN ALL TABLES OF DATABASE.    
    BEGIN
        SELECT t.name      AS Table_Name
              ,c.name      AS COLUMN_NAME
        FROM   sys.tables  AS t
               INNER JOIN sys.columns c
                    ON  t.OBJECT_ID = c.OBJECT_ID
        WHERE  c.name LIKE '%'+@strFind+'%'
        ORDER BY
               Table_Name
    END
END

ตอนนี้ - ตั้งคีย์สั้นดังต่อไปนี้

ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้นครั้งต่อไปเมื่อใดก็ตามที่คุณต้องการค้นหาข้อความโดยเฉพาะอย่างยิ่งในใด ๆ ของสี่วัตถุเช่นStore procedure, Views, และFunctions Tablesคุณเพียงแค่ต้องเขียนคำหลักนั้นและกดปุ่มทางลัด

ตัวอย่างเช่น:ฉันต้องการค้นหา 'PaymentTable' แล้วเขียน 'PaymentTable' และตรวจสอบให้แน่ใจว่าคุณเลือกหรือไฮไลต์คำหลักที่เขียนในตัวแก้ไขแบบสอบถามแล้วกดปุ่มทางลัดctrl+4- มันจะให้ผลลัพธ์เต็ม


ใน SSMS v18.2 (เชื่อมต่อกับ Azure) รหัสใช้งานได้ดี ปุ่มลัดจะส่งคืนProcedure or function 'Searchinall' expects parameter '@strFind', which was not suppliedแนวคิดใด ๆ
gordon613

@ gordon613 คุณพิมพ์คำค้นหาเพื่อกดคีย์ลัดบน SSMS หรือไม่? เช่น 'ลูกจ้าง' และจากนั้น ctrl + 4
pedram

@ pedram.- ขอบคุณ! ตอนนี้ฉันได้มันทำงาน - ในการตั้งค่าของคุณคุณต้องพิมพ์คำหลักแล้วเน้นมันแล้วกด CTRL + 4
gordon613

ใช่เราต้องเน้น / เลือกคำหลักก่อนกด CTRL + 4 ขอบคุณเพื่อน!
pedram

1
@AngelWarrior คุณอาจลองใช้มันในแบบสอบถามใหม่และควรจะได้ผลสำหรับคุณ ฉันหมายถึงไม่จำเป็นต้องรีสตาร์ท SSMS ขอขอบคุณที่แจ้งเส้นทางลัดสำหรับ SSMS 18.2
pedram


19

โปรดใช้สิ่งนี้เป็นทางเลือก "สกปรก"แต่สิ่งนี้ช่วยฉันได้หลายครั้งโดยเฉพาะเมื่อฉันไม่คุ้นเคยกับโครงการ DB บางครั้งคุณพยายามค้นหาสตริงภายใน SP ทั้งหมดและลืมว่าตรรกะที่เกี่ยวข้องบางอย่างอาจซ่อนอยู่ระหว่างฟังก์ชั่นและทริกเกอร์หรืออาจเป็นคำที่แตกต่างจากที่คุณคิด

จาก MSSMS ของคุณคุณสามารถคลิกขวาที่ฐานข้อมูลของคุณและเลือก Tasks -> Generate Scriptsตัวช่วยสร้างเพื่อส่งออก SPs, Fns และทริกเกอร์ทั้งหมดไปยังไฟล์. sql ไฟล์เดียว

ป้อนคำอธิบายรูปภาพที่นี่

อย่าลืมเลือก Triggers ด้วย!

ป้อนคำอธิบายรูปภาพที่นี่

จากนั้นใช้ Sublime หรือ Notepadเพื่อค้นหาสตริงที่คุณต้องการค้นหา ฉันรู้ว่านี่อาจเป็นวิธีที่ค่อนข้างไร้ประสิทธิภาพและหวาดระแวง แต่ก็ใช้งานได้ :)


12

คุณสามารถใช้อันนี้:

SELECT * 
FROM INFORMATION_SCHEMA.ROUTINES 
WHERE ROUTINE_DEFINITION like '%Search_String%'

9
-1 ข้อเสียเมื่อเทียบกับตัวเลือกอื่นนั้นจะINFORMATION_SCHEMA.ROUTINES.ROUTINE_DEFINITIONเก็บได้เพียง 4,000 ตัวอักษรแรกเท่านั้น
Shannon Severance

8

มันอาจช่วยคุณได้!

SELECT DISTINCT 
      A.NAME AS OBJECT_NAME,
      A.TYPE_DESC
      FROM SYS.SQL_MODULES M 
      INNER JOIN SYS.OBJECTS A ON M.OBJECT_ID = A.OBJECT_ID
      WHERE M.DEFINITION LIKE '%['+@SEARCH_TEXT+']%'
      ORDER BY TYPE_DESC


6
SELECT DISTINCT 
   o.name AS Object_Name,
   o.type_desc
FROM sys.sql_modules m        INNER JOIN        sys.objects o 
     ON m.object_id = o.object_id WHERE m.definition Like '%[String]%';

5

นอกจากนี้คุณสามารถใช้:

SELECT OBJECT_NAME(id) 
    FROM syscomments 
    WHERE [text] LIKE '%flags.%' 
    AND OBJECTPROPERTY(id, 'IsProcedure') = 1 
    GROUP BY OBJECT_NAME(id)

นั่นรวมถึงความคิดเห็น


3
คุณไม่ควรใช้ syscomments เพื่อจุดประสงค์นี้ ฟิลด์ข้อความตัดที่ 4000 อักขระ เขตข้อมูลคำจำกัดความจาก sys.sql_modules ดูเหมือนจะเก็บข้อความทั้งหมด (มากกว่า 4000 ต่อไป)
jlnorsworthy

5
 SELECT DISTINCT OBJECT_NAME([id]),[text] 

 FROM syscomments   

 WHERE [id] IN (SELECT [id] FROM sysobjects WHERE xtype IN 

 ('TF','FN','V','P') AND status >= 0) AND  

 ([text] LIKE '%text to be search%' ) 

OBJECT_NAME ([id]) -> ชื่อวัตถุ (ดูขั้นตอนการจัดเก็บฟังก์ชันสเกลาร์ชื่อฟังก์ชันตาราง)

id (int) = หมายเลขประจำตัววัตถุ

xtype ถ่าน (2) ประเภทวัตถุ สามารถเป็นหนึ่งในประเภทวัตถุต่อไปนี้:

FN = ฟังก์ชันสเกลาร์

P = กระบวนงานที่เก็บไว้

V = ดู

TF = ฟังก์ชั่นตาราง


4

ฉันสร้างขั้นตอนเพื่อค้นหาข้อความในขั้นตอน / ฟังก์ชันตารางมุมมองหรืองาน พารามิเตอร์แรก @search คือเกณฑ์การค้นหา @ เป้าหมายเป้าหมายการค้นหาเช่นขั้นตอนตาราง ฯลฯ หากไม่ได้ระบุให้ค้นหาทั้งหมด @db คือการระบุฐานข้อมูลเพื่อค้นหาเริ่มต้นกับฐานข้อมูลปัจจุบันของคุณ นี่คือแบบสอบถามของฉันใน SQL แบบไดนามิก

ALTER PROCEDURE [dbo].[usp_find_objects]
(
    @search VARCHAR(255),
    @target VARCHAR(255) = NULL,
    @db VARCHAR(35) = NULL
)
AS

SET NOCOUNT ON;

DECLARE @TSQL NVARCHAR(MAX), @USEDB NVARCHAR(50)

IF @db <> '' SET @USEDB = 'USE ' + @db
ELSE SET @USEDB = ''

IF @target IS NULL SET @target = ''

SET @TSQL = @USEDB + '

DECLARE @search VARCHAR(128) 
DECLARE @target VARCHAR(128)

SET @search = ''%' + @search + '%''
SET @target = ''' + @target + '''

IF @target LIKE ''%Procedure%'' BEGIN
    SELECT o.name As ''Stored Procedures''
    FROM SYSOBJECTS o 
    INNER JOIN SYSCOMMENTS c ON o.id = c.id
    WHERE c.text LIKE @search
        AND o.xtype IN (''P'',''FN'')
    GROUP BY o.name
    ORDER BY o.name
END

ELSE IF @target LIKE ''%View%'' BEGIN
    SELECT o.name As ''Views''
    FROM SYSOBJECTS o 
    INNER JOIN SYSCOMMENTS c ON o.id = c.id
    WHERE c.text LIKE @search
        AND o.xtype = ''V''
    GROUP BY o.name
    ORDER BY o.name
END

/* Table - search table name only, need to add column name */
ELSE IF @target LIKE ''%Table%'' BEGIN
    SELECT t.name AS ''TableName''
    FROM sys.columns c 
    JOIN sys.tables t ON c.object_id = t.object_id
    WHERE c.name LIKE @search
    ORDER BY TableName
END

ELSE IF @target LIKE ''%Job%'' BEGIN
    SELECT  j.job_id,
        s.srvname,
        j.name,
        js.step_id,
        js.command,
        j.enabled 
    FROM    [msdb].dbo.sysjobs j
    JOIN    [msdb].dbo.sysjobsteps js
        ON  js.job_id = j.job_id 
    JOIN    master.dbo.sysservers s
        ON  s.srvid = j.originating_server_id
    WHERE   js.command LIKE @search
END

ELSE BEGIN 
    SELECT o.name As ''Stored Procedures''
    FROM SYSOBJECTS o 
    INNER JOIN SYSCOMMENTS c ON o.id = c.id
    WHERE c.text LIKE @search
        AND o.xtype IN (''P'',''FN'')
    GROUP BY o.name
    ORDER BY o.name

    SELECT o.name As ''Views''
    FROM SYSOBJECTS o 
    INNER JOIN SYSCOMMENTS c ON o.id = c.id
    WHERE c.text LIKE @search
        AND o.xtype = ''V''
    GROUP BY o.name
    ORDER BY o.name

    SELECT t.name AS ''Tables''
    FROM sys.columns c 
    JOIN sys.tables t ON c.object_id = t.object_id
    WHERE c.name LIKE @search
    ORDER BY Tables

    SELECT  j.name AS ''Jobs''
    FROM    [msdb].dbo.sysjobs j
    JOIN    [msdb].dbo.sysjobsteps js
        ON  js.job_id = j.job_id 
    JOIN    master.dbo.sysservers s
        ON  s.srvid = j.originating_server_id
    WHERE   js.command LIKE @search
END
'

EXECUTE sp_executesql @TSQL

อัปเดต: หากคุณเปลี่ยนชื่อโพรซีเดอร์จะอัพเดตเท่านั้นsysobjectsแต่จะไม่syscommentsเก็บชื่อเดิมไว้ดังนั้นโพรซีเดอร์นั้นจะไม่รวมอยู่ในผลลัพธ์การค้นหาเว้นแต่ว่าคุณจะลบและสร้างโพรซีเดอร์นั้นใหม่


ตามลิงค์นี้เพื่อค้นหาชื่อคอลัมน์
Weihui Guo

3

ใช้CHARINDEX :

SELECT DISTINCT o.name AS Object_Name,o.type_desc
FROM sys.sql_modules m 
INNER JOIN sys.objects  o 
ON m.object_id=o.object_id
WHERE CHARINDEX('[ABD]',m.definition) >0 ;

ใช้PATINDEX :

SELECT DISTINCT o.name AS Object_Name,o.type_desc
FROM sys.sql_modules m 
INNER JOIN sys.objects  o 
ON m.object_id=o.object_id
WHERE PATINDEX('[[]ABD]',m.definition) >0 ; 

ใช้คู่[[]ABD]นี้คล้ายกับการหลบหนี:

WHERE m.definition LIKE '%[[]ABD]%'



1

แบบสอบถามนี้เป็นข้อความค้นหาในกระบวนงานที่เก็บไว้จากฐานข้อมูลทั้งหมด

DECLARE @T_Find_Text VARCHAR(1000) = 'Foo'

IF OBJECT_ID('tempdb..#T_DBNAME') IS NOT NULL DROP TABLE #T_DBNAME
IF OBJECT_ID('tempdb..#T_PROCEDURE') IS NOT NULL DROP TABLE #T_PROCEDURE

CREATE TABLE #T_DBNAME
(
    IDX int IDENTITY(1,1) PRIMARY KEY 
    , DBName VARCHAR(255)
)

CREATE TABLE #T_PROCEDURE
(
    IDX int IDENTITY(1,1) PRIMARY KEY 
    , DBName VARCHAR(255)
    , Procedure_Name VARCHAR(MAX)
    , Procedure_Description VARCHAR(MAX)
)

INSERT INTO #T_DBNAME (DBName)
SELECT name FROM master.dbo.sysdatabases

DECLARE @T_C_IDX INT = 0
DECLARE @T_C_DBName VARCHAR(255)
DECLARE @T_SQL NVARCHAR(MAX)
DECLARE @T_SQL_PARAM NVARCHAR(MAX) 

SET @T_SQL_PARAM =   
    '   @T_C_DBName VARCHAR(255)
        , @T_Find_Text VARCHAR(255)
    '  


WHILE EXISTS(SELECT TOP 1 IDX FROM #T_DBNAME WHERE IDX > @T_C_IDX ORDER BY IDX ASC)
BEGIN

    SELECT TOP 1 
    @T_C_DBName = DBName 
    FROM #T_DBNAME WHERE IDX > @T_C_IDX ORDER BY IDX ASC

    SET @T_SQL = ''

    SET @T_SQL = @T_SQL + 'INSERT INTO #T_PROCEDURE(DBName, Procedure_Name, Procedure_Description)'
    SET @T_SQL = @T_SQL + 'SELECT SPECIFIC_CATALOG, ROUTINE_NAME, ROUTINE_DEFINITION '
    SET @T_SQL = @T_SQL + 'FROM ' + @T_C_DBName +  '.INFORMATION_SCHEMA.ROUTINES  '
    SET @T_SQL = @T_SQL + 'WHERE ROUTINE_DEFINITION LIKE ''%''+ @T_Find_Text + ''%'' '
    SET @T_SQL = @T_SQL + 'AND ROUTINE_TYPE = ''PROCEDURE'' '

    BEGIN TRY
        EXEC SP_EXECUTESQL  @T_SQL, @T_SQL_PARAM, @T_C_DBName, @T_Find_Text
    END TRY
    BEGIN CATCH
        SELECT @T_C_DBName + ' ERROR'
    END CATCH

    SET @T_C_IDX = @T_C_IDX + 1
END

SELECT IDX, DBName, Procedure_Name FROM #T_PROCEDURE ORDER BY DBName ASC

0
/* 
    SEARCH SPROCS & VIEWS

    The following query will allow search within the definitions 
    of stored procedures and views.

    It spits out the results as XML, with the full definitions, 
    so you can browse them without having to script them individually.

*/

/*
   STEP 1: POPULATE SEARCH KEYS. (Set to NULL to ignore)
*/
DECLARE 
    @def_key varchar(128) = '%foo%',      /* <<< definition search key */
    @name_key varchar(128) = '%bar%',     /* <<< name search key       */
    @schema_key varchar(128) = 'dbo';     /* <<< schema search key     */

;WITH SearchResults AS (
    /* 
       STEP 2: DEFINE SEARCH QUERY AS CTE (Common Table Expression)
    */
    SELECT 
        [Object].object_id                       AS [object_id],    
        [Schema].name                            AS [schema_name], 
        [Object].name                            AS [object_name],
        [Object].type                            AS [object_type],
        [Object].type_desc                       AS [object_type_desc],
        [Details].definition                     AS [module_definition]
    FROM  
        /* sys.sql_modules = where the body of sprocs and views live */
        sys.sql_modules AS [Details] WITH (NOLOCK)
    JOIN
        /* sys.objects = where the metadata for every object in the database lives */
        sys.objects AS [Object] WITH (NOLOCK) ON [Details].object_id = [Object].object_id
    JOIN 
        /* sys.schemas = where the schemas in the datatabase live */
        sys.schemas AS [Schema] WITH (NOLOCK) ON [Object].schema_id = [Schema].schema_id
    WHERE 
        (@def_key IS NULL OR [Details].definition LIKE @def_key)      /* <<< searches definition */
        AND (@name_key IS NULL OR [Object].name LIKE @name_key)       /* <<< searches name       */
        AND (@schema_key IS NULL OR [Schema].name LIKE @schema_key)   /* <<< searches schema     */
)
/* 
   STEP 3: SELECT FROM CTE INTO XML
*/

/* 
    This outer select wraps the inner queries in to the <sql_object> root element 
*/
SELECT 
(
    /* 
        This inner query maps stored procedure rows to <procedure> elements
    */
    SELECT TOP 100 PERCENT
        [object_id]                            AS [@object_id], 
        [schema_name] + '.' + [object_name]    AS [@full_name],
        [module_definition]                    AS [module_definition]
    FROM
        SearchResults
    WHERE
        object_type = 'P'
    ORDER BY
        [schema_name], [object_name]
    FOR XML
        PATH ('procedure'), TYPE
) AS [procedures],  /* <<< as part of the outer query, 
                           this alias causes the <procedure> elements
                           to be wrapped within the <procedures> element */
(
    /* 
        This inner query maps view rows to <view> elements
    */
    SELECT TOP 100 PERCENT 
        [object_id]                            AS [@object_id], 
        [schema_name] + '.' + [object_name]    AS [@full_name],
        [module_definition]                    AS [module_definition]
    FROM
        SearchResults
    WHERE
        object_type = 'V'
    ORDER BY
        [schema_name], [object_name]
    FOR XML
        PATH ('view'), TYPE
) AS [views]  /* <<< as part of the outer query, 
                     this alias causes the <view> elements
                     to be wrapped within the <views> element */
FOR XML 
    PATH ('sql_objects')

0

บ่อยครั้งที่ฉันใช้สคริปต์นี้เพื่อค้นหาว่าโพรเซสใดที่จะแก้ไขหรือหาสิ่งที่ใช้คอลัมน์ของตารางหรือตารางนั้นเลยเพื่อลบขยะเก่า มันตรวจสอบแต่ละฐานข้อมูลเกี่ยวกับอินสแตนซ์ที่มันรันโดย sp_msforeachdb ที่ให้มาอย่างน่าอัศจรรย์

if object_id('tempdb..##nothing') is not null
    drop table ##nothing

CREATE TABLE ##nothing
(
    DatabaseName varchar(30),
    SchemaName varchar(30),
    ObjectName varchar(100),
    ObjectType varchar(50)
)

EXEC master.sys.sp_msforeachdb 
'USE ?
insert into ##nothing
SELECT 
db_name() AS [Database],
[Scehma]=schema_name(o.schema_id), 
o.Name, 
o.type 
FROM sys.sql_modules m
INNER JOIN sys.objects o
    ON o.object_id = m.object_id
WHERE 
    m.definition like ''%SOME_TEXT%'''  
--edit this text

SELECT * FROM ##nothing n
order by OBJECTname 

โอ้ใช่แล้ว ... เป็นอย่างนั้น!
Bitcoin Murderous Maniac

0
-- Applicable for SQL 2005+
USE YOUR_DATABASE_NAME //;
    GO

SELECT [Scehma] = schema_name(o.schema_id)
    ,o.NAME
    ,o.type
FROM sys.sql_modules m
INNER JOIN sys.objects o ON o.object_id = m.object_id
WHERE m.DEFINITION LIKE '%YOUR SEARCH KEYWORDS%'
GO

-2

คุณยังสามารถใช้

CREATE PROCEDURE [Search](
    @Filter nvarchar(max)
)
AS
BEGIN

SELECT name
FROM   procedures
WHERE   definition LIKE '%'+@Filter+'%'

END

จากนั้นเรียกใช้

exec [Search] 'text'

ส่วนคำสั่ง WHERE ไม่มีการอ้างอิงฟิลด์ คุณควรอ้างอิง schema (sys)
Roger Willcocks
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.