SQL: порожній рядок та значення NULL


72

Я знаю, що ця тема трохи суперечлива, і в Інтернеті пливе багато різноманітних статей / думок. На жаль, більшість з них припускають, що людина не знає, в чому різниця між NULL і порожнім рядком. Таким чином вони розповідають історії про дивовижні результати приєднаннях / агрегатах і, як правило, роблять трохи більш досконалі уроки SQL. Роблячи це, вони абсолютно пропускають всю суть і тому для мене марні. Тож сподіваємось, що це запитання та всі відповіді перемістять тему трохи вперед.

Припустимо, у мене є таблиця з особистими даними (ім’я, народження тощо), де один із стовпців - це адреса електронної пошти з типом varchar. Ми припускаємо, що чомусь деякі люди можуть не захотіти надати адресу електронної пошти. Під час вставлення таких даних (без електронної пошти) у таблицю є два доступні варіанти: встановити комірку на NULL або встановити порожній рядок (''). Припустимо, що я знаю про всі технічні наслідки вибору одного рішення над іншим, і я можу створити правильні SQL запити для будь-якого сценарію. Проблема навіть тоді, коли обидва значення різняться на технічному рівні, на логічному рівні вони однакові. Подивившись на NULL і "" Я прийшов до єдиного висновку: я не знаю електронної адреси хлопця. Крім того, як би я не намагався, Мені не вдалося надіслати електронний лист, використовуючи NULL або порожню рядок, тому, мабуть, більшість SMTP-серверів там згодні з моєю логікою. Тому я схильний використовувати NULL там, де я не знаю значення і вважаю порожню рядок поганою справою.

Після декількох напружених дискусій з колегами, я прийшов з двома питаннями:

  1. я маю рацію припускати, що використання порожнього рядка для невідомого значення змушує базу даних "брехати" про факти? Якщо бути більш точним: використовуючи уявлення SQL про те, що є значенням, а що ні, я можу прийти до висновку: у нас є електронна адреса, лише з'ясувавши, що вона не є нульовою. Але згодом, намагаючись надіслати електронний лист, я прийду до суперечливого висновку: ні, у нас немає електронної адреси, що @! # $ База даних, мабуть, бреше!

  2. Чи є логічний сценарій, коли порожній рядок '' може бути таким хорошим носієм важливої ​​інформації (окрім значення та без значення), який було б складно / неефективно зберігати будь-яким іншим способом (наприклад, додатковим стовпцем). Я бачив багато повідомлень, які стверджують, що іноді добре використовувати порожню рядок разом з реальними значеннями та NULL, але поки що не бачив сценарій, який був би логічним (з точки зору дизайну SQL / DB).

PS Деякі люди будуть спокушатись відповісти, що це лише питання особистого смаку. Я не згоден. Для мене це дизайнерське рішення з важливими наслідками. Тож я хотів би побачити відповіді, де думка про це підкріплюється якимись логічними та / або технічними причинами.


11
Ви знаєте , що в Oracle, порожній рядок є NULL?
user281377

8
@ammoQ: Лікування струн нульової довжини Oracle нестандартне. До того ж ''навіть у Oracle це не те саме, що NULL. Наприклад, призначення CHAR(1)стовпця значення ''призведе до ' '(тобто пробілу), а не NULL. Крім того, якби Яцек використовував Oracle, це питання, швидше за все, навіть не виникне :-)
Дін Хардінг

2
Дін: Ви маєте рацію напівкоксу (1) , наприклад, але це ще один WTF, так як має '' IS NULLзначення trueв PL / SQL.
користувач281377

"я маю рацію припускати, що використання порожнього рядка для невідомого значення змушує базу даних" брехати "про факти?" якщо користувачів вашого бізнесу не хвилює невідоме або порожнє, чи брехня навіть має значення?
Енді

Якщо вам потрібно пройти маршрут використання рядка ... будь ласка, переконайтеся, що він порожній. Заради всіх розробників не дозволяйте рядку з пробілом у ньому представляти невідоме вам значення. Прошу тебе
Airn5475

Відповіді:


83

Я б сказав, що NULLце правильний вибір для "немає адреси електронної пошти". Є багато "недійсних" адрес електронної пошти, а "" (порожній рядок) - лише одна. Наприклад, "foo" не є дійсною адресою електронної пошти, "a @ b @ c" недійсна тощо. Тому лише тому, що "" не є дійсною адресою електронної пошти, це не є підставою використовувати її як значення "немає адреси електронної пошти".

Я думаю, ти маєш рацію сказати, що "" не є правильним способом сказати "я не маю значення для цього стовпця". "" - значення.

Прикладом того, де "" може бути дійсне значення, окремим NULLможе бути прізвище людини. Не у кожного є прізвище, тому вам потрібно розмежовувати "немає прізвища" ("" - порожня рядок) та "я не знаю, чи має ця людина прізвище чи ні" ( NULL). Напевно, існує багато інших прикладів, коли порожня рядок як і раніше є дійсним значенням стовпця.


5
Повністю згоден. NULL є з якоїсь причини. ВИБІРТЕ КУХНУ (*) ІЗ ВАШОГО МІСЦЕ, ЩО ЕЛЕКТРОННА ПОШТА Є [НЕ] NULL - це спосіб зробити це, а не порівняння рядків, яке, як правило, буде повільніше (навіть для порожніх рядків, я думаю, але я не впевнений у цьому :).
LudoMC

5
Я думаю, NULLце не означає, що немає адреси електронної пошти, я думаю, це означає, що адреса електронної пошти наразі невідома, невідома, або неможливо заповнити її з інших причин. На щастя, напевно, немає ситуації, коли б не хотілося зберігати в базі даних інформацію про людей, яких справді немає та не планують мати жодної адреси електронної пошти, інакше, можливо, буде потрібно окреме булеве поле.
Олексій

9
@Alexey - NULL означає, що значення немає. Як зазначають інші, порожня рядок - це значення.
Рамхаунд

3
@Ramhound, я погоджуюся, що порожня рядок - це значення, і що NULL невиразно означає "немає значення". Я щойно пояснив своє тлумачення "без цінності". На мою думку, це не те саме, що "особа не відкрила жодного облікового запису електронної пошти". Це швидше "жодна адреса електронної пошти, записана для цієї людини".
Олексій

5
@Ramhound NULL означає, що значення немає. Людина без прізвища там не має значення. Тому NULL слід використовувати і в середньому початковому стовпчику ... Що абсолютно протилежно аргументу, представленому у цій відповіді.
Ізката

41

Погоджуючись з вищезазначеними коментарями, я б додав цей аргумент як основну мотивацію:

  1. Кожному програмісту, який дивиться на базу даних, очевидно, що поле з позначкою NULL - це необов'язкове поле. (тобто запис не вимагає даних для цього стовпця)
  2. Якщо ви позначаєте поле НЕ НУЛЬНИЙ, будь-який програміст повинен інтуїтивно вважати, що це обов'язкове поле.
  3. У полі, яке дозволяє нулі, програмістам слід очікувати, що вони побачать нулі, а не порожні рядки.

Для самостійного документування інтуїтивного кодування використовуйте NULL замість порожніх рядків.


4
+1 Це аргумент "найменшого здивування" щодо розробників проти порожніх рядків. Жоден розробник, який з’явиться пізніше, ніколи не очікував би, що порожні рядки будуть використані для відображення "без електронної адреси".
Томас

6

У вашому прикладі, якщо це значення безпосередньо з веб-поля - я б використовував порожню рядок. Якщо користувач міг би вказати, що він не хоче надавати електронну пошту, або може видалити її - тоді NULL.

Ось посилання на пункти, які ви можете розглянути: https://stackoverflow.com/questions/405909/null-vs-empty-when-dealing-with-user-input/405945#405945

--- відредаговано (У відповідь на коментар Томаса) ---

Бази даних не живуть без додатків, які ними користуються. Визначення NULL або "" не мають значення, якщо програма не може використовувати його належним чином.

Розглянемо один приклад, коли користувач заповнює форму LONG і натискає клавішу Enter, яка надсилатиме постійний запит на сервер. Він міг бути посеред введення електронної пошти. Найімовірніше, ви хочете зберігати все, що він має, у полі електронної пошти, щоб пізніше він міг закінчити це. Що робити, якщо він ввів лише одного персонажа? Що робити, якщо він ввів один символ і потім видалив його? Коли електронна пошта не потрібна, іноді користувачі хочуть її видалити: найпростіший спосіб просто очистити поле. Також у випадку, коли електронна пошта не потрібна, варто перевірити її перед надсиланням.

Ще один приклад: користувач надає електронну пошту як spamto @ [bigcompany] .com - у цьому випадку немає необхідності надсилати електронну пошту, навіть якщо вона існує та є дійсною (і може навіть існувати). Надсилання одного такого, можливо, дешевого, але якщо для щоденних підписок є 10K користувачів з такими електронними листами, то така перевірка може заощадити багато часу.


7
-1. Незалежно від того, веде базу даних веб-сайт чи ні, не має значення. Проектування баз даних відрізняється від веб-дизайну. База даних повинна бути розроблена для збору фактів про бізнес-домен незалежно від інтерфейсу, який використовується для його запису. За вашою логікою, чи слід використовувати нулі, якщо випадково перша програма є виконуваним? Що станеться, якщо перший додаток - це веб-додаток, а наступне - це мобільний додаток? Створіть базу даних для фіксації фактів, використовуючи правила нормалізації, та розробіть веб-сайт для запису до нього.
Томас

Я радий, що ти навчився писати та коментувати цей сайт :) Я все ще вважаю, що БД повинен підтримувати додаток, який ним користується. Перевір мою відредаговану відповідь.
Костянтин Петрухнов

4
Бази даних не живуть без додатків, які ними користуються. На мій досвід, це просто неправда і недалекоглядне. Майже завжди база даних використовується поза програмою, для якої вона була розроблена. Загалом, бази даних виживають довше, ніж програми, для яких вони були побудовані. Бази даних повинні бути розроблені для збору фактів про бізнес, а інтерфейс користувача повинен бути побудований для читання та запису в базу даних, а не навпаки. Реляційний дизайн - це зовсім інший спосіб мислення, ніж дизайн додатків.
Томас

2
Приклади , коли база даних не використовується виключно в оригінальній заявці: звіти, інтеграція з іншими системами.
Томас

1
Як зазначив Томас, БД можуть і часто використовуються більш ніж однією програмою, що додає ваги ідеї збереження даних БД в чистоті. Якщо ви не хочете / не можете обробляти NULL у вашій програмі, ви можете просто замінити їх на "магічні значення" (приємний опис Томаса) на рівні доступу до даних. Таким чином, будь-яким майбутнім програмам, які хочуть отримати доступ до БД, не потрібно знати / відповідати магічним значенням оригінальних програм.
bendemes

5

Я думаю, що відповідь Діна Гардінга висвітлює це дуже добре. Це означає, що я хотів би зазначити, що, говорячи про NULL та порожні рядки на рівні БД, ви повинні подумати про ваші інші типи даних. Чи зберігатимете ви мінімальну дату, коли дата не вказана? або -1, коли не вводиться int? Збереження значення, коли у вас немає значення, означає, що вам доведеться відслідковувати цілий спектр не значень. Принаймні один для кожного типу даних (можливо більше, оскільки ви отримуєте випадки, коли -1 - фактичне значення, тому вам потрібно мати альтернативу тощо). Якщо вам потрібно / хочете зробити щось "вигадливе" на рівні програми, це одне, але їх немає необхідності забруднювати ваші дані.


2
+1 - Це те, що я називаю "рішення магічного значення". Ми повинні придумати магічне значення для кожного типу даних, щоб представити відсутність значення. Крім того, в деяких стовпцях загальне магічне значення є або стає законним значенням, і тому потрібно нове магічне значення.
Томас

5

На жаль, Oracle переплутав подання рядка VARCHAR довжиною нульової із поданням NULL. Обидва вони представлені внутрішньо одним байтом зі значенням нуль. Це ускладнює дискусію набагато складніше.

Дуже багато плутанини навколо NULL зосереджується навколо тризначної логіки . Розглянемо наступний псевдокод:

if ZIPCODE = NULL
    print "ZIPCODE is NULL"
else if ZIPCODE <> NULL
    print "ZIPCODE is not NULL"
else print "Something unknown has happened"

Ви не очікували б третього повідомлення, але саме це ви отримаєте за трьома ціннісними логіками. Три цінні логіки ведуть людей до численних помилок.

Іншим джерелом плутанини є виведення висновків через відсутність даних, як, наприклад, виведення собаки, яка не гавкала вночі. Часто ці умовиводи були не тим, що автор NULL мав намір провести.

Сказавши це, існує безліч ситуацій, коли NULL справляється з відсутністю даних просто чудово, і створює саме потрібні результати. Одним із прикладів є зовнішні ключі у необов'язкових зв’язках. Якщо ви використовуєте NULL для позначення жодного зв’язку в заданому рядку, цей рядок випаде з внутрішнього з'єднання так, як ви очікували.

Також пам’ятайте, що навіть якщо ви повністю уникаєте NULLS у збережених даних (шоста нормальна форма), якщо ви виконайте будь-які зовнішні з'єднання, вам все одно доведеться впоратися з NULLS.


4

Використовуйте Null.

Немає сенсу зберігати значення '', коли просто зробимо поле в таблиці нульовим. Це також робить запити більш очевидними.

Який запит SQL є більш очевидним і читабельним, якщо ви хочете знайти користувачів з електронною адресою?

  1. SELECT * FROM Users WHERE email_address != ''

  2. SELECT * FROM Users WHERE email_address IS NOT NULL

  3. SELECT * FROM Users WHERE email_address != '' and email_address IS NOT NULL

Я б сказав 2 є. Хоча 3 є більш надійним у випадках, коли зберігаються погані дані.

У випадку з адресою електронної пошти у формі, яка є необов’язковою, вона повинна бути відображена і в таблиці. У SQL це нульове поле, а це означає, що воно не відоме.

Я не можу думати про якусь розумну ділову цінність для зберігання порожнього рядка в таблиці, крім просто поганого дизайну. Це як зберігання значення рядка "NULL" або "BLANK", а розробники припускають, що це нульовий або порожній рядок. Для мене це поганий дизайн. Навіщо зберігати це, коли є NULL ??

Просто використовуйте NULL, і ви зробите всіх трохи щасливішими.

БІЛЬШЕ ІНФОРМАЦІЇ:

SQL використовує три цінні логічні системи: True, False та Unknown.

Для кращого та детальнішого пояснення я рекомендую розробникам прочитати: SQL Queries - понад TRUE та FALSE .


3

для конкретного технічного питання питання не є нульовим проти порожнього рядка, це помилка перевірки . Порожня рядок не є дійсною адресою електронної пошти!

на філософське запитання відповідь схожа: підтвердіть свої дані. Якщо порожній рядок є дійсним значенням для відповідного поля, тоді очікуйте його та кодуйте його; якщо ні, використовуйте null.

Порожній рядок був би вагомим вкладом для відповіді на питання: Що мім сказав жирафі?


Навіть при найкращому намірі у світі перевірка може не вирішити цю проблему - він, можливо, повинен буде використовувати метод обробки рядків, коли всі стовпці повинні бути надані з якимсь значенням. У такому випадку залишатиметься питання - яке значення використовувати, коли немає значення? І відповідь, звичайно, буде: значення, яке не означає значення. У БД це звичайно NULL.
jmoreno

2

Я міг би придумати причину виникнення NULL та порожнього рядка:

  • У вас є дійсні адреси електронної пошти: me@example.com
  • У вас немає жодного (і, мабуть, слід попросити його): NULL
  • Ви знаєте, що ця особа не має електронної адреси: Empty String.

Однак я б не рекомендував цього і використовував окреме поле для того, щоб запитати, чи знаєте ви, що жодне не існує.


1

Як я розумію, питання про те, які інтерпретації NULL та порожнього рядка слід вибрати. Це залежить від того, у скільки станах може знаходитися часткове поле.

Інтерпретація залежить від способу доступу до бази даних. Якщо в коді є шар, який повністю витягує базу даних, цілком прийнятним є вибір будь-якої політики (включаючи двома). (Очевидно, що документування політики важливо, однак). Однак якщо доступ до бази даних здійснюється в декількох місцях, то вам слід використовувати дуже просту схему, оскільки код буде складніше підтримувати і в цьому випадку може бути помилковим.


1

Ну в основному на логічному рівні немає різниці між значенням "недійсне" та "відсутність вводу користувача", більшість часу вони просто "особливі випадки". Випадок помилки.

Маючи null займає додатковий простір: ceil (column_with_null / 8) у байтах / на рядок.

Порожня клітинка та null - це спосіб позначити щось не так / має бути за замовчуванням. Навіщо вам потрібно 2 "неправильних" стану? Навіщо використовувати NULL, якщо вони займають додатковий простір і означають точно так само, як порожні рядки? Це просто призведе до плутанини та надмірності, коли у вас є дві речі, що означають (це може означати) абсолютно однаково, легко забути, що ви повинні використовувати NULLs замість порожніх рядків (якщо, наприклад, користувач заборонив деякі поля).

І ваші дані можуть стати безладом. У ідеальному світі ви б сказали, що "дані будуть завжди правильними, і я пам’ятаю" ... але коли люди повинні працювати в команді, а не всі точно на вашому рівні, не рідкість бачити, де (аа. xx <> '' І bb.zz НЕ НУЛЬНИЙ)

Тому замість того, щоб виправляти членів моєї команди через день, я просто виконую просте правило. Ні нульових значень, НІКОЛИ!

Підрахунок значень NON-NULL швидше ... Просте запитання - для чого вам це потрібно?


Я туманно пригадую, що десь читав, що використання NULL - це фактично вартість (як з точки зору обчислення, так і зберігання) для бази даних. Тож гарний момент у формулюванні цієї формули.
Яцек Прусія

Не забувайте, що для VARCHARстовпця буде потрібно щонайменше 1 байт для збереження довжини рядка, навіть якщо він дорівнює нулю.
dan04

Порожня клітинка та нуль є обома способами позначити щось не так . Неправда. Нуль - це спосіб вказати відсутність значення. Я думаю, що більшість RDBMS використовують бітовий масив у кожному рядку, щоб вказати, які стовпці є нульовими. Таким чином, додатковий простір настільки крихітний, що не має ніякого значення. Турбуватися про додаткову обробку - це передчасна оптимізація і не буде нічого порівняно зі скачками швидкості, створеними для інших розробників, щоб «виявити», що ви навмисно використовували порожні рядки.
Томас

3
Немає нульових значень . Це підхід страуса. "Ми засунемо голову в пісок і оголосимо, що відсутніх значень не існує". Зазвичай це призводить до рішення Magic Value, де вам потрібно придумати магічне значення для кожного типу даних, щоб відобразити відсутність значення.
Томас

1

Я схильний розглядати це не з точки зору БД, а з програмної точки зору. Я знаю, що це питання стосується клацання SQL, але насправді, скільки користувачів вже зараз отримують доступ до даних?

У програмі мені не подобається нуль / нічого. Є кілька винятків, але вони є саме такими. І ці винятки - це лише погані реалізації.

Отже, якщо користувач не помістив повідомлення в електронній пошті, він повинен мати щось, що визначає, чи це дійсно, чи ні. Якщо порожнє повідомлення електронної пошти добре, то він відображає порожній рядок. Якщо користувач не вказав електронний лист і це порушує правило, об’єкт повинен вказати це.

Ідея нуля, яка має значення, - це стара школа, і це те, про що мають працювати сучасні програмісти.

Навіть у дизайні БД чому поле електронної пошти не може дозволити нулі та мати рядок нульової довжини та інше поле, яке вказує, чи користувач щось вводить? Це один біт, який потрібно запитати в СУБД? На мою думку, БД не повинен обробляти ні логіку бізнесу, ні логіку відображення. Він не був побудований для цього і, таким чином, робить дуже погану роботу з цим.


чому не може поле електронної пошти не допускати нулів і мати рядок нульової довжини - простіше кажучи: адже будь-який розробник, який щось знає про бази даних, ніколи не очікував, що порожні рядки мають магічне значення. Ви намагаєтеся зробити своє власне магічне значення, щоб представити те, що принципово вже існує у кожній базі даних: концепція, яка представляє відсутність значення. Навіщо винаходити колесо? Крім того, ідея NULLS далека, далека від старої школи. Нулі є основою для розуміння дизайну реляційних баз даних.
Томас

ЛОЛ. Як я вже говорив, з точки зору програмістів, нулі майже завжди є болем у задці і майже ніколи не потрібні для БІЗНЕС-ЛОГІКИ. Я особисто як розробник не дуже переймаюся реляційним дизайном. Якби я це зробив, я був би чуваком у БД. Якщо я отримую нуль від БД, я майже завжди перетворюю його на щось раціональне, як порожній рядок, то нехай мій славний дизайн OOP робить це магією. Рамка піклується про ці дурно недійсні сили DBA, що діють на весь світ. Я знаю, що хлопці з БД мають справу з цим, і я відчуваю вас. Але мені як програмісту не доводиться. У мене є кращі рішення.
ElGringoGrande

Вам "ніколи" не доведеться мати справу з нулями. Отже, те, що ви описуєте, - це страусовий розчин у поєднанні з рішенням магічного значення. "Я проігнорую той факт, що відсутні значення, і я перетворять усі нульові цілі числа в -1". Поки не настає день, коли -1 - це реальна цінність. Слід зазначити, що однією з причин MS додала генеричні засоби до .NET полягати у вирішенні масивної невідповідності між базами даних та кодом додатків, і головним чином оберталася навколо вираження нулів у коді середнього рівня. Ці "нерозумні нулі" існують і в бізнес-логіці.
Томас

Те, що деяке ціле число відсутнє в db (або є null), не означає, що я повинен представляти його з -1 або evan nullable (int). Якщо ви думаєте, що це єдиний спосіб боротися з нулями, то ви не дуже добре розумієте програмування. Пам'ятайте, що null - це не те саме, що нічого. Як ви вже говорили, null являє собою місце місця для відсутніх значень у якійсь структурі даних. Це щось означає. Бізнес-логіка рідко (яка не така, як ніколи) потребує цієї концепції, оскільки йдеться про поведінку, а не про дані. І коли це робить недійсним, рідко найкращий спосіб представити це.
ElGringoGrande

Навіть бізнес-логіка повинна враховувати (тобто представляти) відсутні цінності, і це правда в моєму досвіді майже в кожній системі, яку я бачив або будував за останні 20 років. База даних моделює факти, що підлягають захопленню та зберіганню. Якщо бізнес-логіка хоче мати можливість взаємодіяти з базою даних, вона повинна знати, як боротися з нулями. Будь то власна структура, магічна цінність або загальне значення не має значення. Бізнес-логіка потребує здатності керувати отриманням відсутнього значення з бази даних та здатності відзначати значення, яке відсутнє в базі даних.
Томас

-1

Я не думаю, що це має велике значення, але мені подобається краще, коли NULL є.

Коли я переглядаю дані, що відображаються в таблиці (як, наприклад, в SQL Server Management Studio), я можу краще розрізнити відсутнє значення, якщо воно говорить про NULL, а фон має інший колір.

Якщо я бачу порожній пробіл, мені завжди цікаво, чи він справді порожній чи є якийсь пробіл чи якісь невидимі символи. З NULL з першого погляду він гарантовано порожній.

введіть тут опис зображення

Зазвичай я не розрізняю значення в програмі, оскільки це несподівано і дивно, що NULL і порожня рядок означатиме щось інше. І більшу частину часу я використовую оборонний підхід і просто маю справу з обома державами. Але для мене як людини людину NULL простіше обробляти при перегляді даних.


це, здається, не пропонує нічого суттєвого щодо пунктів, викладених та пояснених у попередніх 12 відповідях
грунт

@gnat: Я не згоден, ніхто у відповідях ще не згадував про аспект перегляду даних людьми. Існує лише одне значення NULL, але може бути безліч значень, схожих на порожній рядок (не лише пробіл, але також є і безліч дивовижних символів unicode). Я не бачу жодної іншої відповіді, яка б згадувала цей аспект питання.
Том Пажорек

наскільки я можу сказати, це було досить добре викладено у другій верхній відповіді , опублікованій 5 років тому: "Це очевидно для будь-якого програміста, який дивиться в базу даних ..." тощо.
gnat

@gnat: Я бачу вашу думку, хоча я думаю, що автор не означає те саме. Я вважаю, що він більше про те, що NULL має на увазі необов'язкові поля, але порожній рядок може бути використаний і для необхідних полів, тому NULL є більш логічним для відсутнього значення. Я згоден з ним. Але моя відповідь вказує на той факт, що порожній рядок не такий однозначний, як значення NULL, тому що багато речей можуть виглядати як порожні рядки з першого погляду, а насправді не є порожніми рядками.
Том Пажурек
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.