ประสิทธิภาพ TSQL - เข้าร่วมกับมูลค่าระหว่างต่ำสุดและสูงสุด


10

ฉันมีสองตารางที่ฉันเก็บ:

  • ช่วง IP - ตารางการค้นหาประเทศ
  • รายการคำขอมาจาก IP ที่ต่างกัน

IP ถูกเก็บไว้เป็นbigints เพื่อปรับปรุงประสิทธิภาพการค้นหา

นี่คือโครงสร้างของตาราง:

create table [dbo].[ip2country](
    [begin_ip] [varchar](15) NOT NULL,
    [end_ip] [varchar](15) NOT NULL,
    [begin_num] [bigint] NOT NULL,
    [end_num] [bigint] NOT NULL,
    [IDCountry] [int] NULL,
    constraint [PK_ip2country] PRIMARY KEY CLUSTERED 
    (
        [begin_num] ASC,
        [end_num] ASC
    )
)

create table Request(
    Id int identity primary key, 
    [Date] datetime, 
    IP bigint, 
    CategoryId int
)

ฉันต้องการรับคำขอแยกตามประเทศดังนั้นฉันจึงดำเนินการค้นหาดังนี้

select 
    ic.IDCountry,
    count(r.Id) as CountryCount
from Request r
left join ip2country ic 
  on r.IP between ic.begin_num and ic.end_num
where r.CategoryId = 1
group by ic.IDCountry

ฉันมีบันทึกจำนวนมากในตาราง: ประมาณ 200,000 ในIP2Countryและไม่กี่ล้านในRequestดังนั้นแบบสอบถามใช้เวลาสักครู่

ดูแผนการดำเนินการส่วนที่แพงที่สุดคือการค้นหาดัชนีแบบกลุ่มบนดัชนี PK_IP2Country ซึ่งดำเนินการหลายครั้ง (จำนวนแถวในคำขอ)

นอกจากนี้บางสิ่งที่ฉันรู้สึกแปลก ๆ เล็กน้อยก็คือleft join ip2country ic on r.IP between ic.begin_num and ic.end_numส่วนหนึ่ง (ไม่รู้ว่ามีวิธีที่ดีกว่าในการค้นหา)

โครงสร้างตารางข้อมูลตัวอย่างและแบบสอบถามบางอย่างมีอยู่ใน SQLFiddle: http://www.sqlfiddle.com/#!3/a463e/3 (น่าเสียดายที่ฉันไม่คิดว่าฉันสามารถแทรกระเบียนจำนวนมากเพื่อสร้างปัญหาได้อีก หวังว่าจะให้ความคิด)

ฉัน (แน่นอน) ไม่ใช่ผู้เชี่ยวชาญในการเพิ่มประสิทธิภาพ / ปรับแต่ง SQL ดังนั้นคำถามของฉันคือ: มีวิธีใดที่ชัดเจนว่าโครงสร้าง / แบบสอบถามนี้สามารถปรับปรุงประสิทธิภาพได้ดีขึ้นหรือไม่


2
ที่อยู่ IP สามารถแมปกับหลายประเทศได้หรือไม่ ถ้าไม่คุณสามารถ จำกัด PK ของคุณให้แคบลงbegin_numได้ ฉันยังต้องเข้าร่วมA BETWEEN B AND Cค่อนข้างบ่อยและฉันอยากรู้ว่ามีวิธีในการบรรลุโดยไม่ต้อง RBAR เข้าร่วม
Jon of All Trades

1
มันเป็นเรื่องเล็กน้อยสำหรับคำถามของคุณ แต่ฉันจะพิจารณาสร้างbegin_ipและend_ipยืนยันคอลัมน์จากการคำนวณเพื่อป้องกันความเป็นไปได้ของข้อความและตัวเลขที่จะไม่ซิงค์กัน
Jon of All Trades

@ w0lf: มีช่วงที่ทับซ้อนกันip2country (begin_num, end_num)หรือไม่
ypercubeᵀᴹ

@JonofAllTrades โดยปกติหนึ่ง IP ควรอยู่ในประเทศเดียวดังนั้นฉันคิดว่าความคิดของคุณเกี่ยวกับข้อความค้นหาเช่นgive me the first record that has a begin_num < ip in asc order of begin_num(แก้ไขฉันถ้าฉันผิด) อาจใช้งานได้และปรับปรุงประสิทธิภาพ
Cristian Lupascu

1
@ w0lf: การแสดงผลของฉันคือว่าโดยทั่วไปแล้วสิ่งที่เซิร์ฟเวอร์ทำในกรณีเช่นนี้เพราะมันจะสแกนเป็นครั้งแรกจากbegin_numนั้นสแกนตามend_numภายในชุดนั้นและค้นหาระเบียนเดียวเท่านั้น
Jon of All Trades

คำตอบ:


3

คุณต้องมีดัชนีเพิ่มเติม ในตัวอย่างซอของคุณฉันเพิ่ม:

CREATE UNIQUE INDEX ix_IP ON Request(CategoryID, IP)

ซึ่งครอบคลุมคุณสำหรับตารางคำขอและรับดัชนีแสวงหาแทนที่จะสแกนดัชนีคลัสเตอร์

มาดูกันว่ามันช่วยปรับปรุงและแจ้งให้ฉันทราบ ฉันเดาว่ามันจะช่วยได้ไม่น้อยตั้งแต่การสแกนดัชนีนั้นฉันแน่ใจว่าไม่ถูก


ฉันไม่รู้ว่าทำไม แต่ผลลัพธ์ดูเหมือนจะแตกต่างกัน (ใน SQLFiddle)
Cristian Lupascu

@ w0lf: มันแตกต่างกัน (probbaly) เพราะคุณทั้งสองแทรกข้อมูลแบบสุ่มลงในตาราง
ypercubeᵀᴹ

@percube แน่นอนว่าเป็นสาเหตุ ฉันทำหลายสิ่งหลายอย่างเมื่อไม่นานมานี้ว่าฉันลืมว่าข้อมูลนั้นสุ่ม ขอโทษ
Cristian Lupascu

2

มีวิธีการบังคับแบบไร้เดียงสาอยู่เสมอ: คุณสามารถระเบิดแผนที่ IP ของคุณได้ เข้าร่วมตารางตัวเลขกับแผนที่ที่มีอยู่ของคุณเพื่อสร้างหนึ่งระเบียนต่อที่อยู่ IP นั่นเป็นเพียง 267K ระเบียนจากข้อมูลซอของคุณไม่มีปัญหาเลย

CREATE TABLE IPLookup
  (
  IP  BIGINT PRIMARY KEY,
  CountryID  INT
  )
INSERT INTO IPLookup (IP, CountryID)
  SELECT
    N.Number, Existing.IDCountry
  FROM
    ip2country AS Existing
    INNER JOIN Numbers AS N ON N.Number BETWEEN Existing.begin_num AND Existing.end_num

สิ่งนี้จะทำให้การค้นหาง่ายขึ้นและหวังว่าจะเร็วขึ้น นี้เพียงทำให้รู้สึกถ้าคุณทำการอัปเดตค่อนข้างไม่กี่ip2countryของหลักสูตร

ฉันหวังว่าคนอื่นจะมีทางออกที่ดีกว่า!


ชุดข้อมูลทั้งหมดจะสร้างมากกว่า 5 พันล้านรายการดังนั้นฉันไม่คิดว่าฉันจะทำ แต่นี่เป็นความคิดที่ดี ฉันแน่ใจว่าเป็นไปได้ในหลาย ๆ กรณีที่คล้ายกัน +1
Cristian Lupascu

0

ลองสิ่งนี้:

SELECT ic.IDCountry,
        COUNT(r.Id) AS CountryCount
FROM Request r
INNER JOIN (SELECT begin_num+NUMS.N [IP], IDCountry 
            FROM ip2country
            CROSS JOIN (SELECT TOP(SELECT ABS(MAX(end_num-begin_num)) FROM ip2country) ROW_NUMBER() OVER(ORDER BY sc.name)-1 [N]
                        FROM sys.columns sc) NUMS
            WHERE begin_num+NUMS.N <= end_num) ic
ON r.IP = ic.IP
WHERE r.CategoryId = 1
GROUP BY ic.IDCountry

ขอบคุณฉันได้ลองวิธีการของคุณ แต่ดูเหมือนว่าจะมีราคาแพงกว่าแบบสอบถามเริ่มต้น
Cristian Lupascu

คุณมีแถวกี่แถวในแต่ละตาราง ผมอยากที่จะทำซ้ำขนาดของปัญหาของคุณในฐานข้อมูลของฉันและพยายามที่จะแก้โดยไม่ต้องเพิ่มดัชนี :)
วินซ์ Pergolizzi

ประมาณ 200,000 ใน IP2Country และอีกสองสามล้าน (อาจเป็นหลายสิบล้านในอนาคตอันใกล้) ในการร้องขอ ฉันคิดว่าถ้าคุณแก้ปัญหาโดยไม่ต้องจัดทำดัชนีคุณควรได้รับชื่อ "DBA แห่งปี" :)
Cristian Lupascu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.