|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Сейчас прорабатываю логику одной задачи не могу решить, какой подход лучше. Есть историческая таблица данных, в которой история задается началом и концом интервала: item, value, date_beg, date_end. Здесь item это изменяемый элемент, value значение в указанном интервале, а date_beg и date_end задают период действия значения. Например есть такие данные: itemvaluedate_begdate_endamount102019-01-142019-01-16amount202019-01-16 Здесь значение amount равно 10 с 14 января, а с 16 января равно уже 20. Допустим мне нужно вставить в эту историческую запись новое значение, действующее с 15 января. Значение можно задать таким образом: Код: plsql 1.
Обновлять таблицы я буду merge-запросом, размножая каждую new-запись нужное количество раз с соответствующими вспомогательными полями (например поле flag='before' для записей перед sysdate, flag='after' для записей после sysdate и flag=null для вставляемой записи). И соответственно в предложении ON одним из условий будет flag is not null, соответственно для вставляемых записей будет использоваться when not matched, а для обновления существующих записей будет использоваться when matched с проверкой значения flag. Вопрос следующий. Если я вставляю новую запись в историческую таблицу и для этой записи есть какие-то будущие значения (в приведенном примере это значение amount=20 с 2019-01-16), то что с ними сделать? Их нужно аннулировать (удалить) или сохранить? Другими словами, какая итоговая таблица должна получится после обновления: itemvaluedate_begdate_endamount102019-01-142019-01-15amount152019-01-15 или itemvaluedate_begdate_endamount102019-01-142019-01-15amount152019-01-152019-01-16amount202019-01-16 ? Я больше склоняюсь к первому варианту (то есть при добавлении новой исторической записи все последующие данные аннулируются). В этом варианте, как мне кажется, будет меньшая неопределенность конечного результата. Но зато второй вариант более способствует сохранению изменений. Не поделитесь своим мнением, как правильнее? ... |
|||
:
Нравится:
Не нравится:
|
|||
15.01.2019, 22:57 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.Но зато второй вариант более способствует сохранению изменений. Плюс второй вариант позволяет обновить данные одним merge-запросом, а для первого варианта потребуется два запроса — delete и merge. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.01.2019, 22:59 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.Не поделитесь своим мнением, как правильнее? Выкинуть из таблицы поле date_end и задача становится тривиальной. Posted via ActualForum NNTP Server 1.5 ... |
|||
:
Нравится:
Не нравится:
|
|||
15.01.2019, 23:04 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Dimitry SibiryakovВыкинуть из таблицы поле date_end и задача становится тривиальной. Но тогда значительно усложнится получение значения, действующего на определенную дату. Я предполагаю, что в этой таблице будет довольно много записей и мне бы хотелось сохранить возможность быстрой выборки действующих значений. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.01.2019, 23:07 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.Но тогда значительно усложнится получение значения, действующего на определенную дату. С чего бы это? Код: sql 1. 2.
Posted via ActualForum NNTP Server 1.5 ... |
|||
:
Нравится:
Не нравится:
|
|||
15.01.2019, 23:13 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Хотя да. Я по привычке зациклился на использовании периода, а использование одной даты все значительно упрощает. Я лучше попробую оптимизировать получение действующих значений, индексами или материализацией. ... |
|||
:
Нравится:
Не нравится:
|
|||
15.01.2019, 23:15 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
https://en.wikipedia.org/wiki/Slowly_changing_dimension читайте и думайте . и вообще про slow changing dimensions разных типов читайте и будет вам счастье:-) ... |
|||
:
Нравится:
Не нравится:
|
|||
15.01.2019, 23:15 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Dimitry SibiryakovС чего бы это? Я не про конкретный способ получения действующих значений (хотя я бы скорее использовал lead). Но разве аналитика или подзапросы не будут более "тяжелыми" для сервера, чем обычная фильтрация по индексированным полям? ... |
|||
:
Нравится:
Не нравится:
|
|||
15.01.2019, 23:16 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B. обычная фильтрация по индексированным полямВ каких случаях поможет индексация второго поля? ... |
|||
:
Нравится:
Не нравится:
|
|||
15.01.2019, 23:26 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Про индексы не знаю. Но я сейчас сравнил два запроса на одной таблице: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12.
В таблице примерно 500 тысяч строк. Делаю такой запрос: Код: plsql 1. 2. 3.
Отрабатывает мгновенно (11 мс), возвращает примерно 50 тысяч строк. План: Код: plaintext 1. 2.
Делаю такой запрос: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8.
Возвращает те же данные, но выполняется более 30 секунд. И даже внутренний подзапрос выполняется более 300 мс. План: Код: plaintext 1. 2. 3. 4. 5. 6.
... |
|||
:
Нравится:
Не нравится:
|
|||
15.01.2019, 23:57 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.мгновенно (11 мс), возвращает примерно 50 тысяч строк. я не знаю, как именно вы выполняли запрос, и измеряли время, поэтому на всякий случай - убедитесь, что за 11 мс вы возвращаете именно 50 тысяч, а не первые 50 из них. например, выполнив Код: plsql 1. 2. 3.
... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 00:43 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.как правильнее? Ответьте хотя бы какой вид версионности Вы строите - техническую или бизнесовую? Если первое, то вопрос не имеет смысла - ситуация невозможна. Если второе - то все определяется не абстрактными соображениями, но лишь требованиями бизнес-процессов. Лично мне не встречались реализации версионности, допускающие создание записей между существующими версиями, и я затрудняюсь таковой придумать. При проектировании схемы обратите внимание на виды SCD. Возможно, под Ваши задачи лучше подойдет какой-то другой вид. Если все-таки остановитесь на SCD2 или гибриде на его основе - то дополнительно рассмотрите поддержку этого вида в SQL. В оракеле она пока пока на уровне "синтаксического сахара", но все-таки появилась. В терадате чуть богаче, но тоже по сути "синтаксический сахар". Однако нельзя исключать, что в будущем сделают более основательную поддержку этого дела. Что касается идеи "убрать верхнюю границу" - идея на самом деле фиговая. Фиговость повылезает на join с другими версионными таблицами не в срезе на момент, но за период. Кроме того, будет сложно отличить историческую запись от актуальной, а это обычно важно. Помимо вышеизложенного, существует вариация, допускающая пропуски версий (объект как бы перестал существовать на какой-то промежуток времени). Я лишь однажды "краем уха рядом постоял", но все-таки. Хотя это тот еще ад в плане написания запросов сложнее выборки на дату. По конечной дате действия актуальной записи: 1. НЕ используйте null. Возьмите константу типа date'3000-01-01' - это существенно упростит жизнь. 2. Если для конечной даты действия использовать (для типа date) дату след. версии "минус секунда" - то предикаты на версии можно писать просто between, что лучше читается, особенно в сложных запросах. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 03:15 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.Есть историческая таблица данных, в которой история задается началом и концом интервала: item, value, date_beg, date_end.История в виде EAV? - Фу. 21681882 ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 07:47 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
andrey_anonymousписать просто betweenОбратить внимание на неуместное использование between несколько больше шансов, чем на неподходящий тип сравниваемого выражения. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 08:09 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
ElicИстория в виде EAV? Почему EAV? Только потому, что в примере столбец называется item, а не item_id? Это таблица токенов доступа, у которых есть определенный срок действия. Если срок действия токена истек, нужно запросить новый. А в таблице хранится их история. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 08:14 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
andrey_anonymousФиговость повылезает на join с другими версионными таблицами не в срезе на момент, но за период. Кроме того, будет сложно отличить историческую запись от актуальной, а это обычно важно. Очень точно сформулировано, спасибо. Да, я в том числе и это имел ввиду под «усложнением». Но зато историчность не ввиде периода, а в виде эффективной даты настолько упрощает вставку записей, что поневоле задумаешься. andrey_anonymousПомимо вышеизложенного, существует вариация, допускающая пропуски версий (объект как бы перестал существовать на какой-то промежуток времени). Да, это было бы не лишним, но обязательным требованием не является. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 08:30 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
кит северных морейубедитесь, что за 11 мс вы возвращаете именно 50 тысяч Я не говорю о возвращении данных, я говорю о выполнении запроса. Если получать результаты запроса (переходом на последнюю строку), то это еще какое-то дополнительное время (несколько меньше секунды), одинаковое для обоих вариантов. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 08:32 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
andrey_anonymousто дополнительно рассмотрите поддержку этого вида в SQL Используется Oracle 10g, поэтому встроенная поддержка версионности отсутствует. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 08:37 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.Используется Oracle 10g, поэтому встроенная поддержка версионности отсутствует.Присутствует. Но твоя задачаAlibek B.Это таблица токенов доступа, у которых есть определенный срок действияотношения к версионности не имеет. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 09:33 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.Другими словами, какая итоговая таблица должна получится после обновления: імхо авторДопустим мне нужно вставить в эту историческую запись новое значение, действующее с 15 января. неточная постановка задания, надо уточнять у пользователей, что имеется ввиду для "мне нужно вставить" должна быть "дата по", если нет, то подразумевается значение по умолчанию (date'3000-01-01') тогда пропадет вопрос, что в результате получить ..... stax ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 09:49 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Staxдля "мне нужно вставить" должна быть "дата по" Это очень осложнит вставку записей одним запросом. Скорее даже одним запросом вставку сделать будет уже невозможно — ведь при задании новой записи с указанным сроком действия в этот диапазон может попасть произвольное количество ранее существующих записей. Как я уже писал в самом начале, я склоняюсь к первому варианту — если вставляется новое значение, то это автоматически аннулирует все запланированные ранее изменения. Это простое правило, которое легко запомнить. Но может быть есть веские аргументы в пользу второго варианта, которые я пока просто упускаю? ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 10:02 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.Staxдля "мне нужно вставить" должна быть "дата по" Это очень осложнит вставку записей одним запросом. Скорее даже одним запросом вставку сделать будет уже невозможно — ведь при задании новой записи с указанным сроком действия в этот диапазон может попасть произвольное количество ранее существующих записей. Как я уже писал в самом начале, я склоняюсь к первому варианту — если вставляется новое значение, то это автоматически аннулирует все запланированные ранее изменения. Это простое правило, которое легко запомнить. Но может быть есть веские аргументы в пользу второго варианта, которые я пока просто упускаю? вы СЕБЕ упрощаете задачу надо смотеть как оно есть в реальной жизни, что нужно пользователю я не знаю что такое "токены доступа" но, по любому, правіло чуть усложнится как будет выглядеть таблица после вставки 'amount' , 30 , 2019-01-20? надо ж не просто вставіть, но и ... ..... stax ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 11:07 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
В первом варианте все максимально просто: 1. delete ... where date_beg > sysdate 2. merge ... То есть первым запросом я удаляю все запланированные записи, а вторым запросом одновременно обновляю действующие записи (выставляю date_end) и добавляю новые записи (date_beg=sysdate). Во втором варианте запрос будет только один (delete не нужен), просто в merge будет более сложное выражение для when matched, предусматривающее как обновление date_end действующих записей, так и изменение date_beg следующей запланированной записи. Если же новые записи вставлять не на определенную дату, а на период, то одним запросом никак не обойтись. Вначале нужно будет удалить запланированные записи, полностью попадающие в период, а затем обновить границы на оставшихся записях, соприкасающихся с новым периодом. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 11:20 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B., как будет выглядеть таблица после вставки 'amount' , 30 , 2019-01-20? ..... stax ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 11:22 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
itemvaluedate_begdate_endamount102019-01-142019-01-16amount202019-01-162019-01-20amount302019-01-20 Под sysdate я имел ввиду дату записи, то есть: delete ... where date_beg > moment update ... set date_end = moment insert ... date_beg = moment ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 11:26 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B., вот видете, уже появился update самое главное, узнайте как устроено в реальной жизни, и от етого исходите зы andrey_anonymous 1. НЕ используйте null. Возьмите константу типа date'3000-01-01' - это существенно упростит жизнь. .... stax ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 11:32 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Staxвот видете, уже появился update Почему "уже"? update и insert были изначально. Просто они были внутри merge, в предложениях when matched и when not matched соответственно. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 11:34 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Stax1. НЕ используйте null. Возьмите константу типа date'3000-01-01' - это существенно упростит жизнь. А почему, не могли бы пояснить? Я и сам об этом задумывался. В случае с MSSQL и вопроса бы такого не возникало, там OR так драматически влияет на запрос, что его желательно избегать при любой возможности. Но в Oracle выражение (date_end is null or date_end > sysdate) при индексации date_end работает быстро, я не замечал, чтобы оно приводило к снижению производительности. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 11:37 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Вариант с указанием диапазона для добавляемой записи не нравится мне вот чем. На схеме зеленым указаны значения до добавления, голубым после добавления. И если с первым примером все довольно просто, то второй и особенно третий пример реализовать сложнее. Нужно или использовать четыре запроса (для каждого из вариантов пересечения диапазонов), либо попытаться все сделать в одном, который получится сложным для понимания и выполнения. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 12:28 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.Staxвот видете, уже появился update Просто они были внутри merge, в предложениях when matched и when not matched соответственно. я с мерже плохо дружу, можете привести пример в целях моего ликбеза зы уж больно витеевато у меня выйдет ..... stax ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 12:47 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B., вопрос не стоит нравится/не нравится что надо пользователю, как оно будет в реальной жизни если Вы УВЕРЕНЫ что напр правок задним числом не будет, то достаточно и только "с" зи насчет null, смысл и в удобстве, Вам почти всюду надо будет лепить or или nvl или через вью .... stax ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 12:53 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
-2-Alibek B. обычная фильтрация по индексированным полямВ каких случаях поможет индексация второго поля? Если история очень-очень богатая, то индекс может помочь на выборке актуального среза :) Хотя в этом случае уместнее partition by dt_end Alibek B.Если срок действия токена истек, нужно запросить новый. А в таблице хранится их история. Для решения этой задачи НЕ требуется городить версионность на периодах актуальности записи по типу SCD2. Скорее даже вредно. Выбирайте что-то вроде SCD4 и радуйтесь жизни. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 13:17 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Staxя с мерже плохо дружу, можете привести пример в целях моего ликбеза Что-то типа такого. Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.
Этот запрос предполагает, что из таблицы предварительно удалены все ранее запланированные записи. Если их не удалять, то в таблице d нужно получать три клона (для добавляемой записи, для действующей записи и для следующей запланированной записи) и предложение when matched будет более сложным. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 13:38 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
andrey_anonymousВыбирайте что-то вроде SCD4 и радуйтесь жизни. Не совсем понял этот совет. SCD4 это ведь SCD2, только не для всех атрибутов, а для выборочных. Ну так у меня так и есть, я буду хранить версии только токенов доступа. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 13:52 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.SCD4 это ведь SCD2, только не для всех атрибутов, а для выборочных. Нет, это отдельная таблица для исторических записей. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 17:31 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Так та таблица, которую я привел в примере, и есть отдельная таблица для исторических записей. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 17:58 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B.Так та таблица, которую я привел в примере, и есть отдельная таблица для исторических записей. Тогда не морочьте мозг, просто вставляйте туда утратившие актуальность записи. Если очень надо выстроить цепочку версий - то введите суррогатный pk и свяжите либо ссылками, либо просто порядком относительно pk. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 18:12 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Alibek B., о, наконец то прочитал что такое SCD https://habr.com/ru/post/101544/ ps ето наш xtender? .... stax ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 18:52 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Staxето наш xtender? Это Саян. ... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 19:52 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
... |
|||
:
Нравится:
Не нравится:
|
|||
16.01.2019, 20:18 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
andrey_anonymousStaxето наш xtender? Это Саян. незнаком, "Sayan Malakshinov" ето Саян? просто я не понял идею с триггером 1) мутация 2) в паралельные сессии не увидят изменения ..... stax ... |
|||
:
Нравится:
Не нравится:
|
|||
17.01.2019, 09:34 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
SYAlibek B., Возможно подкинет какие-нибудь идеи SCD2 . SY. Код: plsql 1. 2. 3.
до вставки фром - то отличаются на 1, после вставки совпадают так задумано? ..... stax ... |
|||
:
Нравится:
Не нравится:
|
|||
17.01.2019, 09:57 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
Stax до вставки фром - то отличаются на 1, после вставки совпадают так задумано? Спасибо, да from должно быть sysdate + 1. SY. ... |
|||
:
Нравится:
Не нравится:
|
|||
17.01.2019, 15:29 |
|
Вставка записи в историческую таблицу - что делать с последующими записями?
|
|||
---|---|---|---|
#18+
SYStaxдо вставки фром - то отличаются на 1, после вставки совпадают так задумано? Спасибо, да from должно быть sysdate + 1. SY. В многопользовательской сред с merge лучше поаккуратнее, можно получить wrong results - если, конечно, не пофиксили багу "микроотката". ... |
|||
:
Нравится:
Не нравится:
|
|||
17.01.2019, 19:49 |
|
|
start [/forum/topic.php?all=1&fid=52&tid=1882922]: |
0ms |
get settings: |
9ms |
get forum list: |
12ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
35ms |
get topic data: |
10ms |
get forum data: |
2ms |
get page messages: |
71ms |
get tp. blocked users: |
1ms |
others: | 13ms |
total: | 159ms |
0 / 0 |