C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Все этот NULL
Nick
Отправлено: 04.06.2004, 17:42


Машинист паровоза

Группа: Участник
Сообщений: 247



Все этот NULL
Столько проблем с ним
может кто то объснит зачем это надо
или для всех полей объявлять нот нул
что бзабота была не моя

ведь если звезды зажигают значит это для кого-то нужно

одно только визуальное преимущество видел
остальное все недостатки (может только мои)
Admin
Отправлено: 04.06.2004, 20:58


Владимир

Группа: Администратор
Сообщений: 1190



QUOTE
Столько проблем с ним


А какие проблемы ?
По моему как раз очень удобно.
И зачаем для всех полей объявлять NOT NULL ?

Представьте себе ситуацию:
Есть 2 таблицы:

Categories — Категории товаров
-------------
ID integer — ключ autoinc
Category varchar(80)
...

и вторая
Goods  — товары
-------------
ID integer — ключ autoinc
ID_Cat integer — внешний ключ на поле ID таблицы Categories
MyGoods varchar(30)
....

SQL
ALTER TABLE "Goods" ADD CONSTRAINT "FK_Goods_Category" FOREIGN KEY ("ID_Cat") REFERENCES "Categories" (ID) ON DELETE CASCADE ON UPDATE CASCADE;


То есть есть товары и есть какие-то категории этих товаров
к которым относятся эти товары.
типа

МОНИТОРЫ: ... ... ... ...
ПРИНТЕРЫ: ... ... ...
...

Если объявить поле ID_Cat как NOT NULL, то товар, который заноситься
в таблицу Goods будет обязательно принадлежать какой-либо
из категорий !
А ведь может быть ситуация, когда товар не должен
относиться ни к одной из категорий, но ограничение внешнего
ключа и NOT NULL не позволят Вам этого сделать !

А если поле ID_Cat будет объявлено как NULL, то в этом случае
товары из таблицы Goods или относятся к какой-либо категории
из таблицы Categories или могут иметь значение NULL,
то есть не относиться ни к одной из категорий.

Тогда и ограничение внешнего ключа (конечно в зависимости
от реальной ситуации) можно записать по другому:

SQL
ALTER TABLE "Goods" ADD CONSTRAINT "FK_Goods_Category" FOREIGN KEY ("ID_Cat") REFERENCES "Categories" (ID) ON DELETE SET NULL ON UPDATE CASCADE;


То есть при удалении какой-либо категории из таблицы категорий,
товар, относящийся к этой категории, не будет удален, а
перейдет в положение NULL — то есть не относящийся ни к одной
из категорий.

И таких ситуаций (и более реальных) бывает достаточно много.

------------------------

Также, если все поля объявить как NOT NULL могут наступить
приличные тормоза при изменении/вставке данных, поскольку,
(если я правильно понимаю), при создании поля типа NOT NULL
создается триггер базы данных, который каждый раз срабатывает
при изменении значения поля, проверяя его.
То есть, сделав все поля NOT NULL, можете получить дополнительные
тормоза при вставке/изменении поля.

Так что советую прежде очень продумать, какие именно поля
должны быть NOT NULL, а к какие нет.

Nick
Отправлено: 08.06.2004, 12:15


Машинист паровоза

Группа: Участник
Сообщений: 247



Ну в категории можно и 0 поставить
а то что условие

if ( kat1 <> kat2 ) // = false если любое из значений NULL

мне очень не пондравилось

пришлось делать функцию mnz (NULL) = 0
и очень во многих местах переделывать условия
типа

if ( (kat1 is null and kat2 is not null) or
(kat1 is not null and kat2 is null) or
(kat1 <> kat2))

вообше-то кажется это правильно на всех языках
в Access было тоже (не совсем уверен)



AVC
Отправлено: 08.06.2004, 12:35


Ветеран

Группа: Модератор
Сообщений: 1583



В большинстве СУБД есть специальная функция типа IsNull(arg1,arg2) возвращающая первый не Null аргумент.
Значение Null нужно, полезно и удобно если уметь с ним работать.
Gedeon
Отправлено: 08.06.2004, 12:53


Ветеран

Группа: Модератор
Сообщений: 1742



QUOTE (AVC @ 08/06/2004, 13:37)
В большинстве СУБД есть специальная функция типа IsNull(arg1,arg2) возвращающая первый не Null аргумент.
Значение Null нужно, полезно и удобно если уметь с ним работать.

Полностью за. Формируйте запросы с помощью таких функций как IsNull там где они Вам не нужны и проблем не будет. Считаю очень удобным например следующее
что-то + NULL = NULL
очень удобно для формирования отчетов.
Nick
Отправлено: 08.06.2004, 13:59


Машинист паровоза

Группа: Участник
Сообщений: 247



Вот я хотел узнать у тех
кто с ним подружился
но так и не въеду

например
чем это замечательно замечательно в отчете

1 + NULL = NULL
^ ^ ^
колонка 1 колонка 2 итоговая колонка

разве это замечательно
ЗП допустим
AVC — 300$
Gedeon — 300$
Nick — нисколько не получил потому что никак не въедет (NULL)
итого ЗП = NULL
AVC
Отправлено: 08.06.2004, 14:11


Ветеран

Группа: Модератор
Сообщений: 1583



А для этого и есть
Итого = IsNull(AVC,0) + IsNull(Gedeon,0) + IsNull(Nik,0) -> 600
Кстати агрегатные функции работают с сумированием правильно.

А, например для number, как вы будете отличать в каких полях есть данные но они равны 0 а в каких их просто нет без Null.
Nick
Отправлено: 08.06.2004, 16:24


Машинист паровоза

Группа: Участник
Сообщений: 247




isnull нет в IBase, FireBird попробовал нет

я пользуюсь своей UDF
вернее двумя mnz(:value_Int) и mnzf(:value_dbl)

Ладно, давайте закроем это дело.

Я открыл его когда, в очередной раз наткнулся
на сравнение
if ( param1 <> param2 )

когда начинал делать базу, забыл про это дело

Deem
Отправлено: 09.06.2004, 09:39


Мастер участка

Группа: Участник
Сообщений: 327



QUOTE (Gedeon @ 08/06/2004, 13:55)
[QUOTE=AVC,08/06/2004, 13:37] Считаю очень удобным например следующее
что-то + NULL = NULL
очень удобно для формирования отчетов.

Ага.... Например, при NAME || NAME2 || SURNAME (грубо говоря) для получения имени одним полем не дай бог отчество (NAME2) не указано. smile.gif

А вобще и я не вьехал в одну весчь: при объединении двух таблиц если в одной из них нет соответствующей записи, то запись первой тоже отпадает. Я извращался по всякому, но оптимального решения не нашел. Хотя знаю , что оно есть.
к примеру (очень примерно):
STUFFS: NAME, ID
SERVICES: SERVICE, DONE, STUFFID

Если обслуживане имеет DONE = 1, то указан спец. А если нет — то NULL.
При выборке
SELECT SERVICE, DONE, NAME FROM STUFFS, SERVICES
WHERE STUFFID = ID
записи из SERVICES без STUFFID выпадают. Тут можно извратиться и ввести спеца 'не указан' c ID = 0, а у ID в STUFFS — умолчательное 0. Да и DONE тоже может пригодиться.
Но бывают и более заваороченные случаи. Раскройте мне глаза, пожалуйста.
AVC
Отправлено: 09.06.2004, 11:45


Ветеран

Группа: Модератор
Сообщений: 1583



QUOTE (Deem @ писал)

при объединении двух таблиц если в одной из них нет соответствующей записи, то запись первой тоже отпадает

Это называется Inner join и используется для объединения по умолчанию. То что вас интересует обзывается термином outer join и может быть right и left. Языковая реализация (способ записи) внешних объединений зависит от сервера (или его настроек) СУБД.
Gedeon
Отправлено: 09.06.2004, 12:10


Ветеран

Группа: Модератор
Сообщений: 1742



Еще хочу добавить, что в самом вопросе указания конкретной БД не было, спорить не буду, есть такие в которых NULL действительно проблема, но возмите SQLServer, Oracle (остальных не знаю вообще или очень поверхностно) там просто достаточно научиться им пользоваться и все.
AVC
Отправлено: 09.06.2004, 12:32


Ветеран

Группа: Модератор
Сообщений: 1583



По примерам Deem

SQL
Select SERVICE, DONE, NAME FROM STUFFS, SERVICES
From Stuff, Services
Where StuffID = ID

Это inner join
Результат — те записи из Stuff для которых есть записи в Services

SQL
Select SERVICE, DONE, NAME FROM STUFFS, SERVICES
From Stuff left outer join Services on (StuffID = ID)

Это left outer join
Результат — все записи из Stuff и записи из Services если они есть. Если в Services нет записей то поля заполнены Null'ами.

правое внешнее объединения аналогично левому.

SQL
Select SERVICE, DONE, NAME FROM STUFFS, SERVICES
From Stuff ,Services

Если память не изменяет это cross join
Полное объединение (декартово произведение) таблиц. Обычно результат ошибки.
Deem
Отправлено: 10.06.2004, 16:40


Мастер участка

Группа: Участник
Сообщений: 327



Ну, значит я ламерюга. Сто раз ковырял этот JOIN, однако понял только, что используется для объединения аналогично where a = b.
Это же офигеть как круто! biggrin.gif и просто. Спасибо. cool.gif
Deem
Отправлено: 11.06.2004, 10:58


Мастер участка

Группа: Участник
Сообщений: 327



Просветите меня окончательно: как три таблы объединить при помощи JOIN? У меня записи размножаются, вроде как связываются только две из трех.

select name, service, ddate
From SPEC SP right join SERVICE SE on (SE.SPECID = SP.ID),
DATE DA right join SERVICE S on (S.ID = DA.SERVICEID)

Там не зпт, видимо, должна стоять.

Отредактировано Deem — 11/06/2004, 12:07
Deem
Отправлено: 11.06.2004, 11:42


Мастер участка

Группа: Участник
Сообщений: 327



Соре, разобрался:

From SPEC SP right join SERVICE SE on (SE.SPECID = SP.ID)
right join DATE DA on (SE.ID = DA.SERVICEID)
AVC
Отправлено: 11.06.2004, 12:45


Ветеран

Группа: Модератор
Сообщений: 1583



На всякий случай.
Синтаксис зависит от СУБД. Вот еще один интересный пример (Sybase):

user posted image
Table A (Aid, A_Name)
1, a1
2, a2
3, a3
4, a4

Table B (Bid, B_Name, Aid)
1, b1, 1(a1)
2, b2, 2(a2)
3, b3, 1(a1)
4, b4, Null

Table C (Cid, C_Name, Aid, Bid)
1, c1, 2(a2), Null
2, c2, 1(a1), Null
3, c3, 1(a1), 2(b2)
4, c4, Null, 3(b3)

Связь A c B и C (бывший ваш вопрос)
Select A.A_Name, B.B_Name, C.C_Name
From
A left outer join B on (A.Aid = B.Aid)
,A left outer join C on (A.Aid = C.Aid)
результат
a1, b1, c2
a1, b1, c3
a1, b3, c2
a1, b3, c3
a2, b2, c1
a3, -, -
a4, -, -

Связь A c B и A с C через B
Select A.A_Name, B.B_Name, C.C_Name
From
A left outer join (B left outer join C on (B.Bid = C.Bid))
on (A.Aid = B.Aid)
результат
a1, b1 -
a1, b3, c4
a2, b2, c3
a3, -, -
a4, -, -


Отредактировано AVC — 11/06/2004, 13:53
Deem
Отправлено: 14.06.2004, 11:08


Мастер участка

Группа: Участник
Сообщений: 327



Вовово. Я такое пытался провеонуть, однако ошибался, видно, в сиснтаксисе.

Хороший пример. smile.gif

Вернуться в Работа с базами данных в C++Builder