ํ•œ SQL ์กฐ์‚ฌ ์ด์•ผ๊ธฐ

์ง€๋‚œ XNUMX์›”์— ์ €๋Š” VWO ์ง€์›ํŒ€์œผ๋กœ๋ถ€ํ„ฐ ํฅ๋ฏธ๋กœ์šด ๋ฒ„๊ทธ ๋ณด๊ณ ์„œ๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ๋Œ€๊ทœ๋ชจ ๊ธฐ์—… ํด๋ผ์ด์–ธํŠธ์— ๋Œ€ํ•œ ๋ถ„์„ ๋ณด๊ณ ์„œ ์ค‘ ํ•˜๋‚˜์˜ ๋กœ๋“œ ์‹œ๊ฐ„์€ ์—„์ฒญ๋‚˜ ๋ณด์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์ด ์ œ๊ฐ€ ๋‹ด๋‹นํ•˜๋Š” ์˜์—ญ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ฆ‰์‹œ ๋ฌธ์ œ ํ•ด๊ฒฐ์— ์ง‘์ค‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

์„ ์‚ฌ ์‹œ๋Œ€

๋‚ด๊ฐ€ ๋งํ•˜๋Š” ๋‚ด์šฉ์„ ๋ช…ํ™•ํ•˜๊ฒŒํ•˜๊ธฐ ์œ„ํ•ด VWO์— ๋Œ€ํ•ด ์กฐ๊ธˆ ๋ง์”€ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์›น์‚ฌ์ดํŠธ์—์„œ ๋‹ค์–‘ํ•œ ํƒ€๊ฒŸ ์บ ํŽ˜์ธ์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. A/B ์‹คํ—˜ ์ˆ˜ํ–‰, ๋ฐฉ๋ฌธ์ž ๋ฐ ์ „ํ™˜ ์ถ”์ , ํŒ๋งค ํผ๋„ ๋ถ„์„, ํžˆํŠธ ๋งต ํ‘œ์‹œ, ๋ฐฉ๋ฌธ ๊ธฐ๋ก ์žฌ์ƒ ๋“ฑ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํ”Œ๋žซํผ์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ์€ ๋ณด๊ณ ์ž…๋‹ˆ๋‹ค. ์œ„์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์€ ์„œ๋กœ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์—… ๊ณ ๊ฐ์˜ ๊ฒฝ์šฐ, ๋ง‰๋Œ€ํ•œ ์–‘์˜ ์ •๋ณด๋Š” ์ด๋ฅผ ๋ถ„์„ ํ˜•์‹์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ํ”Œ๋žซํผ ์—†์ด๋Š” ์ „ํ˜€ ์“ธ๋ชจ๊ฐ€ ์—†์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ”Œ๋žซํผ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ ์„ธํŠธ์— ๋Œ€ํ•ด ๋ฌด์ž‘์œ„ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ์˜ˆ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Chrome์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜(์œ ๋Ÿฝ์— ์œ„์น˜ํ•˜๋ฉฐ iPhone์„ ์‚ฌ์šฉํ•˜๋Š”) ์‚ฌ์šฉ์ž์—๊ฒŒ <date d1>๋ถ€ํ„ฐ <date d2>๊นŒ์ง€ 'abc.com' ํŽ˜์ด์ง€์˜ ๋ชจ๋“  ํด๋ฆญ์ˆ˜๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

๋ถ€์šธ ์—ฐ์‚ฐ์ž์— ์ฃผ์˜ํ•˜์„ธ์š”. ํด๋ผ์ด์–ธํŠธ๋Š” ์ฟผ๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ƒ˜ํ”Œ์„ ์–ป๊ธฐ ์œ„ํ•ด ์ž„์˜๋กœ ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Š๋ฆฐ ์š”์ฒญ

๋ฌธ์ œ์˜ ํด๋ผ์ด์–ธํŠธ๋Š” ์ง๊ด€์ ์œผ๋กœ ๋น ๋ฅด๊ฒŒ ์ž‘๋™ํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค.

"/jobs"๊ฐ€ ํฌํ•จ๋œ URL์ด ์žˆ๋Š” ํŽ˜์ด์ง€๋ฅผ ๋ฐฉ๋ฌธํ•œ ์‚ฌ์šฉ์ž์˜ ๋ชจ๋“  ์„ธ์…˜ ๊ธฐ๋ก์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์ด ์‚ฌ์ดํŠธ์—๋Š” ์—„์ฒญ๋‚œ ์–‘์˜ ํŠธ๋ž˜ํ”ฝ์ด ์žˆ์—ˆ๊ณ  ์šฐ๋ฆฌ๋Š” ์ด ์‚ฌ์ดํŠธ๋ฅผ ์œ„ํ•ด ๋ฐฑ๋งŒ ๊ฐœ๊ฐ€ ๋„˜๋Š” ๊ณ ์œ  URL์„ ์ €์žฅํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๋“ค์€ ๊ทธ๋“ค์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋ชจ๋ธ๊ณผ ๊ด€๋ จ๋œ ๋งค์šฐ ๊ฐ„๋‹จํ•œ URL ํ…œํ”Œ๋ฆฟ์„ ์ฐพ๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋น„์กฐ์‚ฌ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์›๋ž˜์˜ ๋Š๋ฆฐ SQL ์ฟผ๋ฆฌ์ž…๋‹ˆ๋‹ค.

SELECT 
    count(*) 
FROM 
    acc_{account_id}.urls as recordings_urls, 
    acc_{account_id}.recording_data as recording_data, 
    acc_{account_id}.sessions as sessions 
WHERE 
    recording_data.usp_id = sessions.usp_id 
    AND sessions.referrer_id = recordings_urls.id 
    AND  (  urls &&  array(select id from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com/jobs%')::text[]   ) 
    AND r_time > to_timestamp(1542585600) 
    AND r_time < to_timestamp(1545177599) 
    AND recording_data.duration >=5 
    AND recording_data.num_of_pages > 0 ;

ํƒ€์ด๋ฐ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ณ„ํš๋œ ์‹œ๊ฐ„: 1.480ms ์‹คํ–‰ ์‹œ๊ฐ„: 1431924.650ms

์ฟผ๋ฆฌ๋Š” 150๋งŒ ํ–‰์„ ํฌ๋กค๋งํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ ํ”Œ๋ž˜๋„ˆ์—๋Š” ๋ช‡ ๊ฐ€์ง€ ํฅ๋ฏธ๋กœ์šด ์„ธ๋ถ€ ์ •๋ณด๊ฐ€ ํ‘œ์‹œ๋˜์—ˆ์ง€๋งŒ ๋šœ๋ ทํ•œ ๋ณ‘๋ชฉ ํ˜„์ƒ์€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

์š”์ฒญ์„ ๋” ์ž์„ธํžˆ ์—ฐ๊ตฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ณด์‹œ๋‹ค์‹œํ”ผ ๊ทธ๋Š” ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค JOIN ํ…Œ์ด๋ธ” XNUMX๊ฐœ:

  1. ์„ธ์…˜: ๋ธŒ๋ผ์šฐ์ €, ์‚ฌ์šฉ์ž ์—์ด์ „ํŠธ, ๊ตญ๊ฐ€ ๋“ฑ ์„ธ์…˜ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
  2. ๋…น์Œ_๋ฐ์ดํ„ฐ: ๊ธฐ๋ก๋œ URL, ํŽ˜์ด์ง€, ๋ฐฉ๋ฌธ ๊ธฐ๊ฐ„
  3. URL์ด: ๋งค์šฐ ํฐ URL์˜ ์ค‘๋ณต์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ๋ณ„๋„์˜ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ชจ๋“  ํ…Œ์ด๋ธ”์€ ์ด๋ฏธ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ถ„ํ• ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. account_id. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํŠนํžˆ ํฐ ๊ณ„์ • ํ•˜๋‚˜๊ฐ€ ๋‹ค๋ฅธ ๊ณ„์ •์— ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š” ์ƒํ™ฉ์ด ์ œ์™ธ๋ฉ๋‹ˆ๋‹ค.

๋‹จ์„œ๋ฅผ ์ฐพ๊ณ 

์ž์„ธํžˆ ์กฐ์‚ฌํ•ด ๋ณด๋ฉด ํŠน์ • ์š”์ฒญ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์ค„์„ ์ž์„ธํžˆ ์‚ดํŽด๋ณผ ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

urls && array(
	select id from acc_{account_id}.urls 
	where url  ILIKE  '%enterprise_customer.com/jobs%'
)::text[]

์ฒซ ๋ฒˆ์งธ ์ƒ๊ฐ์€ ์•„๋งˆ๋„ ILIKE ์ด ๋ชจ๋“  ๊ธด URL์— ๋Œ€ํ•ด(์šฐ๋ฆฌ๋Š” 1,4๋งŒ ๊ฐœ ์ด์ƒ์˜ ์œ ์ผํ•œ ์ด ๊ณ„์ •์— ๋Œ€ํ•ด ์ˆ˜์ง‘๋œ URL) ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์•„๋‹ˆ์š”, ๊ทธ๊ฒŒ ์š”์ ์ด ์•„๋‹™๋‹ˆ๋‹ค!

SELECT id FROM urls WHERE url ILIKE '%enterprise_customer.com/jobs%';
  id
--------
 ...
(198661 rows)

Time: 5231.765 ms

ํ…œํ”Œ๋ฆฟ ๊ฒ€์ƒ‰ ์š”์ฒญ ์ž์ฒด์—๋Š” 5์ดˆ๋ฐ–์— ๊ฑธ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐฑ๋งŒ ๊ฐœ์˜ ๊ณ ์œ ํ•œ URL์—์„œ ํŒจํ„ด์„ ๊ฒ€์ƒ‰ํ•˜๋Š” ๊ฒƒ์€ ๋ถ„๋ช…ํžˆ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ชฉ๋ก์˜ ๋‹ค์Œ ์šฉ์˜์ž๋Š” ์—ฌ๋Ÿฌ ๋ช…์ž…๋‹ˆ๋‹ค. JOIN. ์•„๋งˆ๋„ ๊ณผ๋„ํ•œ ์‚ฌ์šฉ์œผ๋กœ ์ธํ•ด ์†๋„๊ฐ€ ๋Š๋ ค์กŒ์„๊นŒ์š”? ๋Œ€๊ฐœ JOIN'๋Š” ์„ฑ๋Šฅ ๋ฌธ์ œ์˜ ๊ฐ€์žฅ ํ™•์‹คํ•œ ํ›„๋ณด์ด์ง€๋งŒ ์šฐ๋ฆฌ ์‚ฌ๋ก€๊ฐ€ ์ „ํ˜•์ ์ธ ๊ฒƒ์ด๋ผ๊ณ ๋Š” ์ƒ๊ฐํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

analytics_db=# SELECT
    count(*)
FROM
    acc_{account_id}.urls as recordings_urls,
    acc_{account_id}.recording_data_0 as recording_data,
    acc_{account_id}.sessions_0 as sessions
WHERE
    recording_data.usp_id = sessions.usp_id
    AND sessions.referrer_id = recordings_urls.id
    AND r_time > to_timestamp(1542585600)
    AND r_time < to_timestamp(1545177599)
    AND recording_data.duration >=5
    AND recording_data.num_of_pages > 0 ;
 count
-------
  8086
(1 row)

Time: 147.851 ms

๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ ๋˜ํ•œ ์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ์—ˆ์Šต๋‹ˆ๋‹ค. JOIN๊ฝค ๋น ๋ฅธ ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค.

์šฉ์˜์ž ๋ฒ”์œ„ ์ขํ˜€

๊ฐ€๋Šฅํ•œ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ฟผ๋ฆฌ ๋ณ€๊ฒฝ์„ ์‹œ์ž‘ํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ ํŒ€๊ณผ ๋‚˜๋Š” 2๊ฐ€์ง€ ์ฃผ์š” ์•„์ด๋””์–ด๋ฅผ ๊ฐœ๋ฐœํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ํ•˜์œ„ ์ฟผ๋ฆฌ URL์— EXISTS ์‚ฌ์šฉ: URL์— ๋Œ€ํ•œ ํ•˜์œ„ ์ฟผ๋ฆฌ์— ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š”์ง€ ๋‹ค์‹œ ํ™•์ธํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๋‹ฌ์„ฑํ•˜๋Š” ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํžˆ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. EXISTS. EXISTS ์ˆ˜ ์กฐ๊ฑด๊ณผ ์ผ์น˜ํ•˜๋Š” ์œ ์ผํ•œ ๋ฌธ์ž์—ด์„ ์ฐพ๋Š” ์ฆ‰์‹œ ์ข…๋ฃŒ๋˜๋ฏ€๋กœ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.

SELECT
	count(*) 
FROM 
    acc_{account_id}.urls as recordings_urls,
    acc_{account_id}.recording_data as recording_data,
    acc_{account_id}.sessions as sessions
WHERE
    recording_data.usp_id = sessions.usp_id
    AND  (  1 = 1  )
    AND sessions.referrer_id = recordings_urls.id
    AND  (exists(select id from acc_{account_id}.urls where url  ILIKE '%enterprise_customer.com/jobs%'))
    AND r_time > to_timestamp(1547585600)
    AND r_time < to_timestamp(1549177599)
    AND recording_data.duration >=5
    AND recording_data.num_of_pages > 0 ;
 count
 32519
(1 row)
Time: 1636.637 ms

๊ทธ๋ ‡์ฃ . ๋ž˜ํ•‘๋œ ๊ฒฝ์šฐ ํ•˜์œ„ ์ฟผ๋ฆฌ EXISTS, ๋ชจ๋“  ๊ฒƒ์„ ๋งค์šฐ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋‹ค์Œ ๋…ผ๋ฆฌ์  ์งˆ๋ฌธ์€ ์™œ ์š”์ฒญ์ด JOIN-ami์™€ ํ•˜์œ„ ์ฟผ๋ฆฌ ์ž์ฒด๋Š” ๊ฐœ๋ณ„์ ์œผ๋กœ ๋น ๋ฅด์ง€๋งŒ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ์—„์ฒญ๋‚˜๊ฒŒ ๋Š๋ฆฝ๋‹ˆ๋‹ค.

  • ํ•˜์œ„ ์ฟผ๋ฆฌ๋ฅผ CTE๋กœ ์ด๋™ : ์ฟผ๋ฆฌ ์ž์ฒด๊ฐ€ ๋น ๋ฅธ ๊ฒฝ์šฐ ๋จผ์ € ๋น ๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๊ณ„์‚ฐํ•œ ๋‹ค์Œ ์ด๋ฅผ ๊ธฐ๋ณธ ์ฟผ๋ฆฌ์— ์ œ๊ณตํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

WITH matching_urls AS (
    select id::text from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com/jobs%'
)

SELECT 
    count(*) FROM acc_{account_id}.urls as recordings_urls, 
    acc_{account_id}.recording_data as recording_data, 
    acc_{account_id}.sessions as sessions,
    matching_urls
WHERE 
    recording_data.usp_id = sessions.usp_id 
    AND  (  1 = 1  )  
    AND sessions.referrer_id = recordings_urls.id
    AND (urls && array(SELECT id from matching_urls)::text[])
    AND r_time > to_timestamp(1542585600) 
    AND r_time < to_timestamp(1545107599)
    AND recording_data.duration >=5 
    AND recording_data.num_of_pages > 0;

ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ๋งค์šฐ ๋Š๋ ธ์Šต๋‹ˆ๋‹ค.

๋ฒ”์ธ ์ฐพ๊ธฐ

์ด๋ฒˆ์—๋Š” ๋‚ด ๋ˆˆ์•ž์— ์ž‘์€ ์ผ ํ•˜๋‚˜๊ฐ€ ๋ฒˆ์ฉ์˜€๊ณ  ๋‚˜๋Š” ๋Š์ž„์—†์ด ๊ทธ๊ฒƒ์„ ์˜†์œผ๋กœ ์น˜์›Œ๋ฒ„๋ ธ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋” ์ด์ƒ ๋‚จ์€ ๊ฒƒ์ด ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜๋„ ๊ทธ๋…€๋ฅผ ์‚ดํŽด๋ณด๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์–˜๊ธฐํ•˜๋Š”๊ฑฐ์•ผ && ์šด์˜์ž. ์•ˆ๋…• EXISTS ๋ฐฉ๊ธˆ ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. && ๋Š๋ฆฐ ์ฟผ๋ฆฌ์˜ ๋ชจ๋“  ๋ฒ„์ „์—์„œ ์œ ์ผํ•˜๊ฒŒ ๋‚จ์•„ ์žˆ๋Š” ๊ณตํ†ต ์š”์†Œ์˜€์Šต๋‹ˆ๋‹ค.

๋ณด๊ณ  ๋ฌธ์„œ, ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์„ ๋ณธ๋‹ค && ๋‘ ๋ฐฐ์—ด ์‚ฌ์ด์˜ ๊ณตํ†ต ์š”์†Œ๋ฅผ ์ฐพ์•„์•ผ ํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์›๋ž˜ ์š”์ฒญ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

AND  (  urls &&  array(select id from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com/jobs%')::text[]   )

์ฆ‰, URL์—์„œ ํŒจํ„ด ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•œ ๋‹ค์Œ ๊ณตํ†ต ๊ฒŒ์‹œ๋ฌผ์ด ์žˆ๋Š” ๋ชจ๋“  URL๊ณผ์˜ ๊ต์ฐจ์ ์„ ์ฐพ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ "urls"๋Š” ๋ชจ๋“  URL์ด ํฌํ•จ๋œ ํ…Œ์ด๋ธ”์„ ์ฐธ์กฐํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํ…Œ์ด๋ธ”์˜ "urls" ์—ด์„ ์ฐธ์กฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์†Œ ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. recording_data.

์ด์— ๋Œ€ํ•œ ์˜ํ˜น์ด ์ปค์ง€๋ฉด์„œ &&, ์ƒ์„ฑ๋œ ์ฟผ๋ฆฌ ๊ณ„ํš์—์„œ ์ด์— ๋Œ€ํ•œ ํ™•์ธ์„ ์ฐพ์œผ๋ ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค. EXPLAIN ANALYZE (์ด๋ฏธ ๊ณ„ํš์„ ์ €์žฅํ•ด ๋‘์—ˆ์ง€๋งŒ ์ผ๋ฐ˜์ ์œผ๋กœ ์ฟผ๋ฆฌ ํ”Œ๋ž˜๋„ˆ์˜ ๋ถˆํˆฌ๋ช…๋„๋ฅผ ์ดํ•ดํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค SQL๋กœ ์‹คํ—˜ํ•˜๋Š” ๊ฒƒ์ด ๋” ํŽธํ•ฉ๋‹ˆ๋‹ค.)

Filter: ((urls && ($0)::text[]) AND (r_time > '2018-12-17 12:17:23+00'::timestamp with time zone) AND (r_time < '2018-12-18 23:59:59+00'::timestamp with time zone) AND (duration >= '5'::double precision) AND (num_of_pages > 0))
                           Rows Removed by Filter: 52710

์—์„œ๋งŒ ์—ฌ๋Ÿฌ ์ค„์˜ ํ•„ํ„ฐ๊ฐ€์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. &&. ์ด๋Š” ์ด ์ž‘์—…์ด ๋น„์šฉ์ด ๋งŽ์ด ๋“ค ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ ๋ฒˆ ์ˆ˜ํ–‰๋œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ–ˆ์Šต๋‹ˆ๋‹ค.

์กฐ๊ฑด์„ ๋ถ„๋ฆฌํ•˜์—ฌ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค.

SELECT 1
FROM 
    acc_{account_id}.urls as recordings_urls, 
    acc_{account_id}.recording_data_30 as recording_data_30, 
    acc_{account_id}.sessions_30 as sessions_30 
WHERE 
	urls &&  array(select id from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com/jobs%')::text[]

์ด ์ฟผ๋ฆฌ๋Š” ๋Š๋ ธ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด JOIN-s๋Š” ๋น ๋ฅด๊ณ  ํ•˜์œ„ ์ฟผ๋ฆฌ๋„ ๋น ๋ฆ…๋‹ˆ๋‹ค. ๋‚จ์€ ์œ ์ผํ•œ ๊ฒƒ์€ && ์šด์˜์ž.

์ด๊ฒƒ์€ ๋‹จ์ง€ ํ•ต์‹ฌ ์ž‘์—…์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ํŒจํ„ด์„ ๊ฒ€์ƒ‰ํ•˜๋ ค๋ฉด ํ•ญ์ƒ ๊ธฐ๋ณธ URL ํ…Œ์ด๋ธ” ์ „์ฒด๋ฅผ ๊ฒ€์ƒ‰ํ•ด์•ผ ํ•˜๋ฉฐ ํ•ญ์ƒ ๊ต์ฐจ์ ์„ ์ฐพ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. URL ๋ ˆ์ฝ”๋“œ๋กœ๋Š” ์ง์ ‘ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ด๋Š” ๋‹จ์ง€ ID๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ฒƒ์ผ ๋ฟ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. urls.

ํ•ด๋ฒ•์œผ๋กœ ๊ฐ€๋Š” ๊ธธ

&& ๋‘ ์„ธํŠธ ๋ชจ๋‘ ๊ฑฐ๋Œ€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋Š๋ฆฝ๋‹ˆ๋‹ค. ๊ต์ฒดํ•˜๋ฉด ์ˆ˜์ˆ ์ด ๋น„๊ต์  ๋นจ๋ฆฌ ๋  ๊ฒƒ ๊ฐ™์•„์š” urls ์— { "http://google.com/", "http://wingify.com/" }.

๋‚˜๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  Postgres์—์„œ ๊ต์ฐจ์ ์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. &&, ๊ทธ๋Ÿฌ๋‚˜ ๋งŽ์€ ์„ฑ๊ณต์„ ๊ฑฐ๋‘์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ตญ ์šฐ๋ฆฌ๋Š” ๋ฌธ์ œ๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์„ ๋‚˜์—๊ฒŒ ์ฃผ์‹ญ์‹œ์˜ค. urls URL์ด ํŒจํ„ด๊ณผ ์ผ์น˜ํ•˜๋Š” ์ค„. ์ถ”๊ฐ€ ์กฐ๊ฑด์ด ์—†์œผ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. 

SELECT urls.url
FROM 
	acc_{account_id}.urls as urls,
	(SELECT unnest(recording_data.urls) AS id) AS unrolled_urls
WHERE
	urls.id = unrolled_urls.id AND
	urls.url  ILIKE  '%jobs%'

๋Œ€์‹ ์— JOIN ๋ฐฉ๊ธˆ ํ•˜์œ„ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ํ™•์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. recording_data.urls ์กฐ๊ฑด์„ ์ง์ ‘ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฐฐ์—ด WHERE.

์—ฌ๊ธฐ์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ์€ && ์ฃผ์–ด์ง„ ํ•ญ๋ชฉ์— ์ผ์น˜ํ•˜๋Š” URL์ด ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์กฐ๊ธˆ๋งŒ ์‚ดํŽด๋ณด๋ฉด ์ด ์ž‘์—…์ด ๋ฐฐ์—ด์˜ ์š”์†Œ(๋˜๋Š” ํ…Œ์ด๋ธ”์˜ ํ–‰)๋ฅผ ํ†ตํ•ด ์ด๋™ํ•˜๊ณ  ์กฐ๊ฑด(์ผ์น˜)์ด ์ถฉ์กฑ๋˜๋ฉด ์ค‘์ง€๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ฌด๊ฒƒ๋„ ์ƒ๊ฐ๋‚˜์ง€ ์•Š๋‚˜์š”? ์‘, EXISTS.

์ดํ›„ recording_data.urls ํ•˜์œ„ ์ฟผ๋ฆฌ ์ปจํ…์ŠคํŠธ ์™ธ๋ถ€์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์ผ์ด ๋ฐœ์ƒํ•˜๋ฉด ์šฐ๋ฆฌ๋Š” ์˜ค๋žœ ์นœ๊ตฌ๋กœ ๋Œ์•„๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. EXISTS ํ•˜์œ„ ์ฟผ๋ฆฌ๋ฅผ ๊ทธ๊ฒƒ์œผ๋กœ ๋ž˜ํ•‘ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ฒƒ์„ ์ข…ํ•ฉํ•˜๋ฉด ์ตœ์ข…์ ์œผ๋กœ ์ตœ์ ํ™”๋œ ์ฟผ๋ฆฌ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

SELECT 
    count(*) 
FROM 
    acc_{account_id}.urls as recordings_urls, 
    acc_{account_id}.recording_data as recording_data, 
    acc_{account_id}.sessions as sessions 
WHERE 
    recording_data.usp_id = sessions.usp_id 
    AND  (  1 = 1  )  
    AND sessions.referrer_id = recordings_urls.id 
    AND r_time > to_timestamp(1542585600) 
    AND r_time < to_timestamp(1545177599) 
    AND recording_data.duration >=5 
    AND recording_data.num_of_pages > 0
    AND EXISTS(
        SELECT urls.url
        FROM 
            acc_{account_id}.urls as urls,
            (SELECT unnest(urls) AS rec_url_id FROM acc_{account_id}.recording_data) 
            AS unrolled_urls
        WHERE
            urls.id = unrolled_urls.rec_url_id AND
            urls.url  ILIKE  '%enterprise_customer.com/jobs%'
    );

๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰ ๋ฆฌ๋“œํƒ€์ž„ Time: 1898.717 ms ์ถ•ํ•˜ํ•  ์‹œ๊ฐ„?!?

๊ทธ๋ ‡๊ฒŒ ๋น ๋ฅด์ง€๋Š” ์•Š์•„์š”! ๋จผ์ € ์ •ํ™•์„ฑ์„ ํ™•์ธํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋งค์šฐ ์˜์‹ฌ์Šค๋Ÿฌ์› ๋‹ค. EXISTS ๋” ์ผ์ฐ ์™„๋ฃŒ๋˜๋„๋ก ๋…ผ๋ฆฌ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฏ€๋กœ ์ตœ์ ํ™”๋ฉ๋‹ˆ๋‹ค. ์š”์ฒญ์— ๋ช…ํ™•ํ•˜์ง€ ์•Š์€ ์˜ค๋ฅ˜๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์—ˆ์Šต๋‹ˆ๋‹ค. count(*) ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ์„ธํŠธ์— ๋Œ€ํ•œ ๋Š๋ฆฌ๊ณ  ๋น ๋ฅธ ์ฟผ๋ฆฌ ๋ชจ๋‘์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ฐ์ดํ„ฐ์˜ ์ž‘์€ ํ•˜์œ„ ์ง‘ํ•ฉ์— ๋Œ€ํ•ด ๋ชจ๋“  ๊ฒฐ๊ณผ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ์ง€ ์ˆ˜๋™์œผ๋กœ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ํ…Œ์ŠคํŠธ์—์„œ๋Š” ์ง€์†์ ์œผ๋กœ ๊ธ์ •์ ์ธ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ๊ฒƒ์„ ๊ณ ์ณค์Šต๋‹ˆ๋‹ค!

๊ตํ›ˆ

์ด ์ด์•ผ๊ธฐ์—์„œ ๋ฐฐ์šธ ์ˆ˜ ์žˆ๋Š” ๊ตํ›ˆ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. ์ฟผ๋ฆฌ ๊ณ„ํš์€ ์ „์ฒด ๋‚ด์šฉ์„ ์•Œ๋ ค์ฃผ์ง€๋Š” ์•Š์ง€๋งŒ ๋‹จ์„œ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์ฃผ์š” ์šฉ์˜์ž๊ฐ€ ํ•ญ์ƒ ์‹ค์ œ ๋ฒ”์ธ์€ ์•„๋‹™๋‹ˆ๋‹ค.
  3. ๋Š๋ฆฐ ์ฟผ๋ฆฌ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ๋ณ‘๋ชฉ ํ˜„์ƒ์„ ๊ฒฉ๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  4. ๋ชจ๋“  ์ตœ์ ํ™”๊ฐ€ ๋ณธ์งˆ์ ์œผ๋กœ ํ™˜์›์ ์ธ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.
  5. ์‚ฌ์šฉ EXIST, ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ ์ƒ์‚ฐ์„ฑ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ถœ๋ ฅ

์ฟผ๋ฆฌ ์‹œ๊ฐ„์ด ~24๋ถ„์—์„œ 2์ดˆ๋กœ ๋‹จ์ถ•๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์„ฑ๋Šฅ์ด ์ƒ๋‹นํžˆ ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค! ์ด ๊ธ€์ด ํฌ๊ฒŒ ๋‚˜์™”์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์šฐ๋ฆฌ๊ฐ€ ํ–ˆ๋˜ ๋ชจ๋“  ์‹คํ—˜์€ ํ•˜๋ฃจ๋งŒ์— ์ด๋ฃจ์–ด์กŒ๊ณ , ์ตœ์ ํ™”์™€ ํ…Œ์ŠคํŠธ์— 1,5~2์‹œ๊ฐ„ ์ •๋„ ์†Œ์š”๋œ ๊ฒƒ์œผ๋กœ ์ถ”์ •๋ฉ๋‹ˆ๋‹ค.

SQL์€ ๋‘๋ ค์›Œํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ํ›Œ๋ฅญํ•œ ์–ธ์–ด์ด์ง€๋งŒ, ๋ฐฐ์šฐ๊ณ  ์‚ฌ์šฉํ•ด ๋ณด์‹ญ์‹œ์˜ค. SQL ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋ฐฉ๋ฒ•, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ฟผ๋ฆฌ ๊ณ„ํš์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•, ์ธ๋ฑ์Šค ์ž‘๋™ ๋ฐฉ๋ฒ• ๋ฐ ์ฒ˜๋ฆฌ ์ค‘์ธ ๋ฐ์ดํ„ฐ์˜ ํฌ๊ธฐ๋ฅผ ์ž˜ ์ดํ•ดํ•˜๋ฉด ์ฟผ๋ฆฌ๋ฅผ ๋งค์šฐ ์„ฑ๊ณต์ ์œผ๋กœ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ณ„์†ํ•ด์„œ ๋‹ค์–‘ํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‹œ๋„ํ•˜๊ณ  ์ฒœ์ฒœํžˆ ๋ฌธ์ œ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ๋ณ‘๋ชฉ ํ˜„์ƒ์„ ์ฐพ๋Š” ๊ฒƒ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ๋•Œ ๊ฐ€์žฅ ์ข‹์€ ์ ์€ ์ด์ „์—๋Š” ๋กœ๋“œ๋˜์ง€ ์•Š์•˜๋˜ ๋ณด๊ณ ์„œ๊ฐ€ ์ด์ œ๋Š” ๊ฑฐ์˜ ์ฆ‰์‹œ ๋กœ๋“œ๋˜๋Š” ๋ˆˆ์— ๋„๊ฒŒ ๋ˆˆ์— ๋„๊ฒŒ ์†๋„๊ฐ€ ํ–ฅ์ƒ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํŠน๋ณ„ํžˆ ๊ฐ์‚ฌํ•จ ๋‚ด ๋™์ง€๋“ค ์•„๋””ํ‹ฐ์•ผ ๋ฏธ์‰ฌ๋ผ(Aditya Mishra)์˜ ๋ช…๋ น์— ๋”ฐ๋ผ์•„๋””ํŠธ์•ผ ๊ฐ€์šฐ๋ฃจ ะธ ๋ฐ”๋ฃฌ ๋งํ˜ธํŠธ๋ผ ๋ธŒ๋ ˆ์ธ์Šคํ† ๋ฐ๊ณผ ๋”˜์นด๋ฅด ํŒ๋””๋ฅด ๋งˆ์ง€๋ง‰ ์š”์ฒญ์— ์ž‘๋ณ„ ์ธ์‚ฌ๋ฅผ ํ•˜๊ธฐ ์ „์— ์ค‘์š”ํ•œ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ๊ฒฌํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€