База дадзеных KDB+: ад фінансаў да "Формулы 1"

KDB+, прадукт кампаніі KX - гэта шырока вядомая ў вузкіх колах, выключна хуткая, калоначная база дадзеных, прызначаная для захоўвання часовых шэрагаў і аналітычных вылічэнняў на іх аснове. Першапачаткова яна карысталася (і карыстаецца) вялікай папулярнасцю ў індустрыі фінансаў - яе выкарыстоўваюць усе топ-10 інвестыцыйных банкаў і многія вядомыя хедж-фонды, біржы і іншыя арганізацыі. У апошні час у KX вырашылі пашырыць кліенцкую базу і зараз прапануюць рашэнні і ў іншых абласцях, дзе маецца вялікая колькасць дадзеных, спарадкаваных па часе ці іншым чынам – целікам, біяінфарматыка, вытворчасць і г.д. У тым ліку яны сталі партнёрам каманды Aston Martin Red Bull Racing у "Формуле 1", дзе дапамагаюць збіраць і апрацоўваць дадзеныя з датчыкаў балідаў і аналізаваць тэсты ў аэрадынамічнай трубе. У гэтым артыкуле я жадаю распавесці, якія асаблівасці KDB+ робяць яе звышпрадукцыйнай, чаму кампаніі гатовыя марнаваць на яе вялікія грошы, нарэшце, чаму гэта насамрэч не база дадзеных.
 
База дадзеных KDB+: ад фінансаў да "Формулы 1"
 
У гэтым артыкуле я пастараюся расказаць у цэлым, што ўяўляе з сябе KDB+, якія магчымасці і абмежаванні мае, у чым яе карысць для кампаній, якія жадаюць апрацоўваць вялікія аб'ёмы дадзеных. Я не буду ўдавацца ў дэталі рэалізацыі KDB+ і ў дэталі яе мовы праграмавання Q. Абедзве гэтыя тэмы вельмі шырокія і заслугоўваюць асобных артыкулаў. Шмат інфармацыі па гэтых тэмах можна знайсці на сайце code.kx.com, у тым ліку кнігу па Q - Q For Mortals (гл. спасылку ніжэй).

Некаторыя тэрміны

  • In-memory база даных. База даных, якая захоўвае дадзеныя ў аператыўнай памяці для паскарэння доступу. Вартасці такой базы зразумелыя, а недахопы - магчымасць страты дадзеных, неабходнасць мець шмат памяці на серверы.
  • Калонкавая база даных. База дадзеных, дзе дадзеныя захоўваюцца пакалонкава, а не запіс за запісам. Асноўная добрая якасць такой базы ў тым, што дадзеныя з адной калонкі захоўваюцца разам на дыску і ў памяці, што значна паскарае доступ да іх. Няма неабходнасці грузіць калонкі, якія не выкарыстоўваюцца ў запыце. Асноўны недахоп - складана мадыфікаваць і выдаляць запісы.
  • Часавы шэраг. Дадзеныя з калонкай тыпу дата ці час. Як правіла, для такіх дадзеных важная спарадкаванасць па часе, каб можна было лёгка вызначыць, які запіс папярэднічае ці ідзе за бягучай, ці каб ужываць функцыі, вынік якіх залежыць ад парадку запісаў. Класічныя базы дадзеных пабудаваны на зусім іншым прынцыпе - прадстаўленні сукупнасці запісаў як мноства, дзе парадак запісаў у прынцыпе не вызначаны.
  • Вектар. У кантэксце KDB+ - гэта спіс элементаў аднаго атамарнага тыпу, напрыклад, лікаў. Іншымі словамі, масіў элементаў. Масівы, у адрозненне ад спісаў, можна захоўваць кампактна і апрацоўваць, выкарыстоўваючы вектарныя інструкцыі працэсара.

 

Гістарычная даведка

Кампанія KX была заснавана ў 1993 годзе Артурам Уітні, які да гэтага працаваў у банку Морган Стэнлі над мовай A +, спадчыннікам APL - вельмі арыгінальным і ў свой час папулярнай мовай у фінансавым свеце. Зразумела, у KX Артур працягнуў у тым жа духу і стварыў вектарна-функцыянальную мову K, кіруючыся ідэямі радыкальнага мінімалізму. Праграмы на K выглядаюць як бязладны набор знакаў прыпынку і адмысловых знакаў, сэнс знакаў і функцый залежыць ад кантэксту, і кожная аперацыя нясе ў сабе нашмат больш сэнсу, чым гэта бывае ў звыклых мовах праграмавання. За кошт гэтага праграма на K займае мінімум месца - некалькі радкоў могуць замяніць старонкі тэксту шматслоўнай мовы тыпу Java - і з'яўляецца звышканцэнтраванай рэалізацыяй алгарытму.
 
Функцыя на K, якая рэалізуе большую частку генератара LL1 парсера па зададзенай граматыцы:

1. pp:{q:{(x;p3(),y)};r:$[-11=@x;$x;11=@x;q[`N;$*x];10=abs@@x;q[`N;x]  
2.   ($)~*x;(`P;p3 x 1);(1=#x)&11=@*x;pp[{(1#x;$[2=#x;;,:]1_x)}@*x]  
3.      (?)~*x;(`Q;pp[x 1]);(*)~*x;(`M;pp[x 1]);(+)~*x;(`MP;pp[x 1]);(!)~*x;(`Y;p3 x 1)  
4.      (2=#x)&(@x 1)in 100 101 107 7 -7h;($[(@x 1)in 100 101 107h;`Ff;`Fi];p3 x 1;pp[*x])  
5.      (|)~*x;`S,(pp'1_x);2=#x;`C,{@[@[x;-1+#x;{x,")"}];0;"(",]}({$[".s.C"~4#x;6_-2_x;x]}'pp'x);'`pp];  
6.   $[@r;r;($[1<#r;".s.";""],$*r),$[1<#r;"[",(";"/:1_r),"]";""]]}  

 Гэтую філасофію экстрэмальнай эфектыўнасці пры мінімуме рухаў цела Артур увасобіў і ў KDB+, якая з'явілася ў 2003 году (думаю, зараз зразумела адкуль літара K у назове) і ёсць ні што іншае, як інтэрпрэтатар чацвёртай версіі мовы K. Па-над K дададзеная больш прыемная погляду карыстача версія K пад назвай Q. У Q таксама дададзена падтрымка спецыфічнага дыялекту SQL - QSQL, а ў інтэрпрэтатар - падтрымка табліц, як сістэмнага тыпу дадзеных, сродкаў працы з табліцамі ў памяці і на дыску і да т.п.
 
Такім чынам, з пункта гледжання карыстача KDB+ - гэта проста інтэрпрэтатар мовы Q з падтрымкай табліц і падобных на SQL выразаў у стылі LINQ з C#. Гэта найважнейшае адрозненне KDB+ ад іншых баз дадзеных і галоўнае яе канкурэнтная перавага, якое часта выпускаюць з-пад увагі. Гэта не база дадзеных + дапаможная мова-інвалід, а паўнавартасная магутная мова праграмавання + убудаваная падтрымка функцый базы дадзеных. Гэтае адрозненне будзе гуляць вызначальную ролю пры пераліку ўсіх пераваг KDB+. Напрыклад…
 

Памер

Па сучасных мерках KDB+ мае проста мікраскапічны памер. Гэта ў літаральным сэнсе адзін выкананы файл памерам менш мегабайта і адзін невялікі тэкставы файл з некаторымі сістэмнымі функцыямі. Рэальна - менш аднаго мегабайта і за гэтую праграму кампаніі плацяць дзясяткі тысяч даляраў у год за адзін працэсар на серверы.

  • Такі памер дазваляе KDB+ выдатна сябе адчуваць на любым жалезе – ад мікракампутара Pi да сервераў з тэрабайтамі памяці. На функцыянальнасць гэта ніяк не ўплывае, нават стартуе Q імгненна, што дазваляе яго выкарыстоўваць у тым ліку як скрыптовай мову.
  • Пры такім памеры інтэрпрэтатар Q цалкам змяшчаецца ў кэш працэсара, што паскарае выкананне праграм.
  • Пры такім памеры выкананага файла працэс Q займае нікчэмна мала месцы ў памяці, можна запускаць іх сотнямі. Пры гэтым пры неабходнасці Q можа апераваць і дзясяткамі-сотнямі гігабайт памяці ў рамках аднаго працэсу.

Універсальнасць

Q выдатна падыходзіць для самых розных задач. Працэс Q можа выконваць ролю гістарычнай базы дадзеных і прадастаўляць хуткі доступ да тэрабайт інфармацыі. У нас, напрыклад, ёсць дзясяткі гістарычных баз, у некаторых з якіх адзін несціснуты дзень дадзеных займае больш за 100 гігабайт. Тым не менш, пры разумных абмежаваннях запыт да базы будзе выкананы за дзясяткі-сотні мілісекунд. У цэлым на запыты карыстальнікаў у нас ёсць універсальны таймаўт - 30 секунд - і ён спрацоўвае вельмі рэдка.
 
З той жа лёгкасцю Q можа быць in-memory базай даных. Даданне новых дадзеных да табліц у памяці адбываецца настолькі хутка, што які лімітуе фактарам з'яўляюцца запыты карыстачоў. Дадзеныя ў табліцах захоўваюцца па калонках, а значыць любая аперацыя па калонцы будзе выкарыстоўваць кэш працэсара на поўную магутнасць. У даданне да гэтага ў KX пастараліся рэалізаваць усе базавыя аперацыі тыпу арыфметычных праз вектарныя інструкцыі працэсара, максімізуючы іх хуткасць. Q можа выконваць і задачы не ўласцівыя базам дадзеных - напрыклад, апрацоўваць струменевыя дадзеныя і вылічваць у "рэальным часе" (з затрымкай ад дзясяткаў мілісекунд да некалькіх секунд у залежнасці задачы) розныя якія агрэгуюць функцыі для фінансавых прылад для розных часавых інтэрвалаў ці будаваць мадэль уплыву дасканалай здзелкі на рынак і праводзіць яе прафіляванне практычна адразу пасля яе здзяйснення. У такіх задачах часцей за ўсё асноўную часавую затрымку ўносіць не Q, а неабходнасць сінхранізацыі дадзеных з розных крыніц. Высокая хуткасць дасягаецца дзякуючы таму, што дадзеныя і функцыі, якія іх апрацоўваюць, знаходзяцца ў адным працэсе, а апрацоўка зводзіцца да выканання некалькіх QSQL выразаў і джойнаў, якія не інтэрпрэтуюцца, а выконваюцца бінарным кодам.
 
Урэшце, на Q можна пісаць і любыя сэрвісныя працэсы. Напрыклад, Gateway працэсы, якія аўтаматычна размяркоўваюць запыты карыстальнікаў па патрэбных базах і серверах. Праграміст мае поўную свабоду рэалізаваць любы алгарытм для балансавання, прыярытызацыі, адмоваўстойлівасці, правоў доступу, квот і наогул чаго душы заўгодна. Галоўная праблема тут, што давядзецца ўсё гэта рэалізоўваць самому.
 
Напрыклад, я пералічу, якія тыпы працэсаў ёсць у нас. Усе яны актыўна выкарыстоўваюцца і працуюць сумесна, аб'ядноўваючы ў адно цэлае дзясяткі розных баз, апрацоўваючы дадзеныя з мноства крыніц і абслугоўваючы сотні карыстальнікаў і прыкладанняў.

  • Канектары (feedhandler) да крыніц дадзеных. Гэтыя працэсы выкарыстоўваюць як правіла вонкавыя бібліятэкі, якія грузяцца ў Q. З-інтэрфейс у Q выключна просты і дазваляе без працы стварыць проксі функцыі для любой З/C++ бібліятэкі. Q дастаткова хуткі, каб справіцца, напрыклад, з апрацоўкай патоку FIX паведамленняў з усіх еўрапейскіх сцёкавых біржаў адначасова.
  • Размеркавальнікі дадзеных (tickerplant), якія служаць прамежкавым звяном паміж канектарамі і спажыўцамі. Адначасова, яны пішуць уваходныя дадзеныя ў адмысловы бінарны лог, забяспечваючы ўстойлівасць для спажыўцоў да страт злучэння ці перазапускам.
  • In-memory базы дадзеных (rdb). Гэтыя базы забяспечваюць максімальна хуткі доступ да волкіх свежых дадзеных, захоўваючы іх у памяці. Як правіла, яны назапашваюць дадзеныя ў табліцах на працягу дня і абнуляюць іх уначы.
  • Persist базы даных (pdb). Гэтыя базы забяспечваюць захаванне даных за сённяшні дзень у гістарычную базу. Як правіла, у адрозненне ад rdb, яны не захоўваюць дадзеныя ў памяці, а выкарыстоўваюць спецыяльны кэш на дыску на працягу дня і капіююць дадзеныя апоўначы ў гістарычную базу.
  • Гістарычныя базы (hdb). Гэтыя базы забяспечваюць доступ да даных за папярэднія дні, месяцы і гады. Памер іх (у днях) абмежаваны толькі памерам цвёрдых дыскаў. Дадзеныя могуць размяшчацца дзе заўгодна, у прыватнасці, на розных дысках для паскарэння доступу. Ёсць магчымасць сціскаць дадзеныя, выкарыстоўваючы некалькі алгарытмаў на выбар. Структура базы добра дакументаваная і простая, дадзеныя захоўваюцца пакалонкава ў звычайных файлах, так што іх можна апрацоўваць у тым ліку сродкамі аперацыйнай сістэмы.
  • Базы з агрэгаванай інфармацыяй. Захоўваюць розныя агрэгацыі, як правіла з, групаваныя па назве прылады і інтэрвалу часу. In-memory базы абнаўляюць свой стан пры кожным уваходным паведамленні, а гістарычныя захоўваюць перадвылічаныя дадзеныя для паскарэння доступу да гістарычных дадзеных.
  • Нарэшце, gateway працэсы, якія абслугоўваюць прыкладання і карыстальнікаў. Q дазваляе рэалізаваць цалкам асінхронную апрацоўку ўваходных паведамленняў, размеркаванне іх па базах, праверку правоў доступу і да т.п. Заўважу, што паведамленні не абмяжоўваюцца і часцей за ўсё не з'яўляюцца SQL выразамі, як гэта бывае ў іншых базах дадзеных. Часцей за ўсё SQL выраз утоена ў адмысловай функцыі і канструюецца зыходзячы з параметраў, запытаных карыстачом - праводзіцца канвертаванне часу, фільтраванне, дадзеныя нармалізуюцца (напрыклад, кошт акцый выраўноўваецца, калі была выплата дывідэндаў) і да т.п.

Тыповая архітэктура для аднаго тыпу дадзеных:

База дадзеных KDB+: ад фінансаў да "Формулы 1"

Хуткасць

Хоць Q з'яўляецца інтэрпрэтаванай мовай, гэта адначасова вектарная мова. Гэта азначае, што шматлікія ўбудаваныя функцыі, у прыватнасці, арыфметычныя, прымаюць аргументы любой формы - лікі, вектара, матрыцы, спісы, а ад праграміста чакаецца, што ён будзе рэалізоўваць праграму як аперацыі над масівамі. У такой мове, калі вы складаеце два вектары па мільёне элементаў, ужо не гуляе ролі, што мова інтэрпрэтаваны, складанне будзе рабіцца супераптымізаванай бінарнай функцыяй. Паколькі ільвіная дзель часу ў праграмах на Q сыходзіць на аперацыі з табліцамі, выкарыстоўвалымі гэтыя базавыя вектарызаваныя функцыі, то на вынахадзе маем вельмі прыстойную хуткасць працы, якая дазваляе апрацоўваць велізарны аб'ём дадзеных нават у адным працэсе. Гэта падобна на матэматычныя бібліятэкі ў пітоне - хоць сам пітон мова вельмі няхуткі, у ім ёсць шмат выдатных бібліятэк тыпу numpy, якія дазваляюць апрацоўваць лікавыя дадзеныя са хуткасцю кампіляванай мовы (дарэчы, numpy ідэалагічна блізкая да Q).
 
Апроч гэтага ў KX вельмі старанна падышлі да праектавання табліц і аптымізацыі працы з імі. Па-першае, падтрымліваецца некалькі відаў індэксаў, якія падтрымліваюцца ўбудаванымі функцыямі і могуць быць ужытыя не толькі да калонак табліц, але і да любых вектараў - групоўка, сартаванне, атрыбут унікальнасці і спецыяльная групоўка для гістарычных баз. Індэкс накладваецца элементарна і аўтаматычна карэктуецца пры даданні элементаў у калонку/вектар. Індэксы аднолькава паспяхова могуць накладвацца на калонкі табліц як у памяці, так і на кружэлцы. Пры выкананні QSQL запыту індэксы выкарыстоўваюцца аўтаматычна, калі гэта магчыма. Па-другое, праца з гістарычнымі дадзенымі зроблена праз механізм адлюстравання файлаў АС (memory map). Вялікія табліцы ніколі не грузяцца ў памяць, замест гэтага патрэбныя калонкі адлюстроўваюцца непасрэдна ў памяць і рэальна грузіцца толькі тая іх частка (тут дапамагаюць у тым ліку азначнікі), якая неабходна. Для праграміста няма розніцы, знаходзяцца дадзеныя ў памяці ці не, механізм працы з mmap цалкам утоены ў нетрах Q.
 
KDB+ не рэляцыйная база дадзеных, табліцы могуць утрымоўваць адвольныя дадзеныя, пры гэтым парадак радкоў у табліцы не змяняецца пры даданні новых элементаў і можа і павінен выкарыстоўвацца пры напісанні запытаў. Гэтая асаблівасць востра неабходна для працы з часавымі шэрагамі (дадзеныя з біржаў, тэлеметрыя, логі падзей), таму што калі дадзеныя адсартаваныя па часе, то карыстачу не трэба ўжываць ніякіх SQL трукаў, каб знайсці ў табліцы першы ці апошні па часе радок ці N радкоў , вызначыць які радок ідзе за N-м радком і да т.п. Яшчэ больш спрашчаюцца джойны табліц, напрыклад знайсці для 16000 здзелак VOD.L (Водафон) апошнюю каціроўку ў табліцы з 500 мільёнаў элементаў займае каля секунды на дыску і дзясятак мілісекунд у памяці.
 
Прыклад джойна па часе - quote табліца адлюстроўваецца ў памяць, таму няма неабходнасці паказваць VOD.L ў where, няяўна выкарыстоўваюцца індэкс на sym калонцы і той факт, што дадзеныя адсартаваныя па часе. Амаль усе джойны ў Q - гэта звычайныя функцыі, а не частка select выразы:

1. aj[`sym`time;select from trade where date=2019.03.26, sym=`VOD.L;select from quote where date=2019.03.26]  

Нарэшце, варта адзначыць, што інжынеры ў KX, пачынальна з самага Артура Уітні, рэальна памяшаныя на эфектыўнасці і прадпрымаюць усе намаганні, каб выціснуць максімум са стандартных функцый Q і аптымізаваць найболей частыя паттэрны выкарыстання.
 

Вынік

KDB+ папулярная ў бізнэсу ў першую чаргу дзякуючы сваёй выключнай універсальнасці - яна аднолькава добра служыць і як in-memory база, і як база для захоўвання тэрабайт гістарычных дадзеных, і як платформа для аналізу дадзеных. Дзякуючы таму, што апрацоўка даных адбываецца непасрэдна ў базе, дасягаецца высокая хуткасць працы і эканомія рэсурсаў. Паўнавартасная мова праграмавання, інтэграваная з функцыямі базы дадзеных, дазваляе рэалізаваць на адной платформе ўвесь стэк неабходных працэсаў - ад атрымання дадзеных, да апрацоўкі запытаў карыстачоў.
 

дадатковыя звесткі

Недахопы

Істотным недахопам KDB+/Q з'яўляецца высокі парог уваходжання. Мова мае дзіўны сінтаксіс, некаторыя функцыі моцна перагружаны (value, напрыклад, мае каля 11 варыянтаў выкарыстання). Самае галоўнае, ён патрабуе радыкальна іншага падыходу да напісання праграм. У вектарнай мове неабходна ўвесь час думаць у тэрмінах пераўтварэнняў масіваў, усе цыклы рэалізоўваць праз некалькі варыянтаў функцый map/reduce (якія завуцца adverbs у Q), ніколі не спрабаваць зэканоміць, замяняючы вектарныя аперацыі атамарнымі. Напрыклад, для знаходжання азначніка N-го ўваходжанні элемента ў масіў варта пісаць:

1. (where element=vector)[N]  

хоць гэта выглядае жудасна неэфектыўна па мерках C/Java (= стварае булевы вектар, where вяртае азначнікі true элементаў у ім). Але такі запіс робіць сэнс выразу больш зразумелым і вы выкарыстоўваеце хуткія вектарныя аперацыі замест павольных атамарных. Канцэптуальная розніца паміж вектарнай мовай і астатнімі параўнальная з розніцай паміж імператыўным і функцыянальным падыходамі да праграмавання, і да гэтага трэба быць гатовым.
 
Некаторыя карыстачы таксама бываюць незадаволеныя QSQL. Справа ў тым, што ён толькі падобны на сапраўдны SQL. На справе ж гэта проста інтэрпрэтатар SQL-падобных выразаў, які не падтрымлівае аптымізацыю запытаў. Карыстальнік павінен сам пісаць аптымальныя запыты, прычым на Q, да чаго многія аказваюцца не гатовы. З іншага боку, вядома, заўсёды можна самому напісаць аптымальны запыт, а не належыць на чорную скрыню-аптымізатар.
 
Як плюс кніга па Q - Q For Mortals даступная бясплатна на сайце кампаніі, таксама там сабрана шмат іншых карысных матэрыялаў.
 
Яшчэ адзін вялікі мінус - кошт ліцэнзіі. Гэта дзясяткі тысяч даляраў у год за адзін CPU. Толькі вялікія фірмы могуць дазволіць сабе такія марнаванні. У апошні час KX зрабіла ліцэнзійную палітыку больш гнуткай і дае магчымасць плаціць толькі за час выкарыстання або арандаваць KDB+ у аблоках Google і Амазон. Таксама KX прапануе для запампоўкі бясплатную версію для некамерцыйных мэт (32 бітная версія або 64-бітная па запыце).
 

канкурэнты

Існуе даволі шмат спецыялізаваных баз, пабудаваных на падобных прынцыпах - калоначныя, in-memory, арыентаваныя на вельмі вялікія аб'ёмы дадзеных. Праблема ў тым, што гэта менавіта спецыялізаваныя базы даных. Яркі прыклад – Clickhouse. У гэтай базы дадзеных вельмі падобны на KDB+ прынцып захоўвання дадзеных на дыску і будынкі азначніка, некаторыя запыты яна выконвае хутчэй KDB+, хоць і не істотна. Але нават як база дадзеных Clickhouse з'яўляецца больш спецыялізаванай чым KDB+ – web аналітыка vs адвольныя часовыя шэрагі (гэтае адрозненне вельмі важна – з-за яго, напрыклад, у Clickhouse няма магчымасці выкарыстоўваць спарадкаванасць запісаў). Але, галоўнае, у Clickhouse няма ўніверсальнасці KDB+, мовы, які дазволіў бы апрацоўваць дадзеныя непасрэдна ў базе, а не грузіць іх папярэдне ў асобны дадатак, будаваць адвольныя SQL выразы, ужываць адвольныя функцыі ў запыце, ствараць працэсы не злучаныя з выкананнем функцый гістарычнай базы . Таму складана параўноўваць KDB+ з іншымі базамі, яны могуць быць лепш у асобных сцэнарах выкарыстання ці проста лепш, калі гаворка ідзе аб задачах класічных баз дадзеных, але мне невядомы іншая гэтак жа эфектыўная і ўніверсальная прылада для апрацоўкі часавых дадзеных.
 

Інтэграцыя з Python

Каб спрасціць працу з KDB+ для людзей, не знаёмых з тэхналогіяй, KX стварылі бібліятэкі для цеснай інтэграцыі з Python у рамках аднаго працэсу. Можна як выклікаць любую python-функцыю з Q, так і наадварот - выклікаць любую Q функцыю з Python (у прыватнасці QSQL выразы). Бібліятэкі канвертуюць пры неабходнасці (дзеля эфектыўнасці не заўсёды) дадзеныя з фармату адной мовы ў фармат іншай. У выніку Q і Python жывуць у такім цесным сімбіёзе, што межы паміж імі сціраюцца. У выніку праграміст, з аднаго боку, мае поўны доступ да шматлікіх карысных бібліятэк Python, з іншага боку, ён атрымлівае інтэграваную ў Python хуткую базу для працы з вялікімі дадзенымі, што асабліва карысна тым, хто займаецца машынным навучаннем ці мадэляваннем.
 
Праца з Q у Python:

1. >>> q()  
2.q)trade:([]date:();sym:();qty:())  
3. q)  
4. >>> q.insert('trade', (date(2006,10,6), 'IBM', 200))  
5. k(',0')  
6. >>> q.insert('trade', (date(2006,10,6), 'MSFT', 100))  
7. k(',1')  

Спасылкі

Сайт кампаніі https://kx.com/
Сайт для распрацоўшчыкаў https://code.kx.com/v2/
Кніга Q For Mortals (на англійскай) https://code.kx.com/q4m3/
Артыкулы на тэму ужыванняў KDB+/Q ад супрацоўнікаў kx — https://code.kx.com/v2/wp/

Крыніца: habr.com

Дадаць каментар