| Новости | FAQ | Авторы | Документация | В действии | Библиотека |
| Инструменты | Полезные ссылки | Хостинги | Скачать | Примеры | Форум |
Sanja v.2 07.01.2005 05:06 / 07.01.2005 05:10
Есть у меня форум, написанный на парсере с mysql - сейчас в нём 34 тыс. реплик суммарным весом где-то 26 мегабайт. Для хранения и вывода реплик выводится метод, аналогичный описанному здесь в Примерах, активно сдобренный ^cache[]SELECT replicate(' ', lvl) + subject AS
zagolovok_s_otstupom, author, lvl, cid,
FROM forum_forumdata
where lvl > 0
ORDER BY hierarchy ASCSQL Server наприсует нужное количество пробелов слева от каждой реплики, чтобы получилось симпатичное дерево.-- ------------------------------
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].FK_pidcid')
AND OBJECTPROPERTY(id, N'IsForeignKey') = 1) ALTER TABLE [dbo].[forum_forumdata]
DROP CONSTRAINT FK_pidcid
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'dbo.forum_forumdata')
AND OBJECTPROPERTY(id, N'IsUserTable') = 1) DROP TABLE dbo.forum_forumdata
GO
CREATE TABLE dbo.forum_forumdata
(
-- автнумерация пойдёт с 40K-й реплики (33K уже есть)
-- Десятизначной нумерации реплик для этого форума хватит
-- лет на пятьдесят
cid INT IDENTITY(40000,1) NOT NULL CHECK ( cid <= 9999999999 ),
pid INT CHECK ( pid <= 9999999999 ),
tid INT CHECK ( tid <= 9999999999 ),
lvl INT CHECK ( lvl <= 199 ),
-- высота дерева ограничена 199 уровнями вложенности - на порядок больше
-- необходимого. Если надо, можно увеличить
hierarchy VARCHAR(2000) CHECK ( LEN(hierarchy) <= 1990 ),
subject VARCHAR(255) NOT NULL,
author VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
cookie VARCHAR(255) NOT NULL,
postdate DATETIME NOT NULL,
ipaddr VARCHAR(100),
subscribe CHAR(3) DEFAULT ('no') NOT NULL,
message TEXT,
CONSTRAINT PK_cid PRIMARY KEY CLUSTERED ( cid )
)
GO
ALTER TABLE dbo.forum_forumdata
ADD CONSTRAINT FK_pidcid FOREIGN KEY( pid )
REFERENCES [dbo].[forum_forumdata] ( cid )
ON UPDATE NO ACTION
ON DELETE NO ACTION;
GO
-- К сожалению, в SQL Server у таблицы может быть
-- только один clustered index - будь это не так,
-- следующие два индекса я бы тоже сделал clustered:
CREATE NONCLUSTERED INDEX idx_tid ON dbo.forum_forumdata (tid)
GO
CREATE NONCLUSTERED INDEX idx_hierarchy ON dbo.forum_forumdata (hierarchy)
GO
CREATE NONCLUSTERED INDEX idx_pid ON dbo.forum_forumdata (pid)
GO
CREATE NONCLUSTERED INDEX idx_lvl ON dbo.forum_forumdata (lvl)
GO
CREATE NONCLUSTERED INDEX idx_author ON dbo.forum_forumdata (author)
GO
CREATE NONCLUSTERED INDEX idx_cookie ON dbo.forum_forumdata (cookie)
GO
CREATE NONCLUSTERED INDEX idx_ipaddr ON dbo.forum_forumdata (ipaddr)
GO
IF EXISTS (SELECT * FROM dbo.sysobjects where id = object_id(N'dbo.RemoveItemMoveSubs')
and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure dbo.RemoveItemMoveSubs
GO
-- удаляет реплику, передаёт детей новому родителю
CREATE PROC RemoveItemMoveSubs
@cid int,
@newparent int
AS
UPDATE CHILD
SET pid = @newparent
FROM forum_forumdata AS CHILD JOIN forum_forumdata AS PARENT
ON CHILD.pid = PARENT.cid
WHERE PARENT.cid = @cid
DELETE FROM forum_forumdata
WHERE cid = @cid
GO
IF EXISTS (SELECT * FROM dbo.sysobjects where id =
object_id(N'dbo.RemoveItemUpgradeSubs') AND OBJECTPROPERTY(id, N'IsProcedure') = 1)
DROP PROCEDURE dbo.RemoveItemUpgradeSubs
GO
-- Удаляет реплику и усыновляет "внуков"
CREATE PROC RemoveItemUpgradeSubs
@cid int
AS
UPDATE CHILD
SET pid = PARENT.pid
FROM forum_forumdata AS CHILD JOIN forum_forumdata AS PARENT
ON CHILD.pid = PARENT.cid
WHERE PARENT.cid = @cid
DELETE FROM forum_forumdata WHERE cid = @cid
GO
IF EXISTS (SELECT * FROM dbo.sysobjects where id =
object_id(N'dbo.RemoveSubtree') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
DROP PROCEDURE dbo.RemoveSubtree
GO
-- Удалzет реплику со всеми детишками:
CREATE PROC RemoveSubtree
@cid int
AS
DELETE FROM forum_forumdata
WHERE hierarchy LIKE (SELECT hierarchy
FROM forum_forumdata
WHERE cid = @cid) + '%'
GO
if exists (select * from dbo.sysobjects where id = object_id(N'dbo.zerofill')
AND XTYPE IN (N'FN', N'IF', N'TF')) drop function dbo.zerofill
GO
CREATE FUNCTION dbo.zerofill (@number char(10))
-- К сожалению, в SQL Server нет эквивалента Оракловскому LPAD
-- и MySQL'ному zerofill, поэтому приходится извращаться:
RETURNS char(10)
BEGIN
DECLARE @temp_char CHAR(10)
SET @temp_char = ''
SET @temp_char =
REPLICATE('0',
DATALENGTH(@temp_char) - DATALENGTH(CAST(CAST(@number as int) as varchar(10)))
)
+ CAST(@number AS CHAR)
RETURN @temp_char
END
GO
if exists (select * from dbo.sysobjects where id = object_id(N'dbo.trg_forumdata_insert')
and OBJECTPROPERTY(id, N'IsTrigger') = 1) DROP TRIGGER dbo.trg_forumdata_insert
GO
-- Триггер, который срабатывает при INSERT'е в таблицу
-- (вычисляет lvl, tid и hierarchy)
CREATE TRIGGER trg_forumdata_insert ON forum_forumdata FOR INSERT
AS
DECLARE @numrows AS int
SET @numrows = @@rowcount
IF @numrows > 1
BEGIN
RAISERROR('Многострочные insert-ы не поддерживаются!', 16, 1)
ROLLBACK TRAN
END
ELSE
IF @numrows = 1
BEGIN
UPDATE CHILD
SET lvl =
CASE
WHEN CHILD.pid IS NULL THEN 0
ELSE PARENT.lvl + 1
END
, tid =
CASE
WHEN ((CHILD.pid IS NULL) OR (CHILD.pid = 0)) THEN CHILD.cid
ELSE PARENT.tid
END
, hierarchy =
CASE
WHEN CHILD.pid IS NULL THEN '.'
ELSE PARENT.hierarchy
END
+ dbo.zerofill(
CAST(CHILD.cid AS varchar(10))
) + '.'
FROM forum_forumdata AS CHILD
JOIN inserted AS INS
ON INS.cid = CHILD.cid
LEFT OUTER JOIN forum_forumdata AS PARENT
ON CHILD.pid = PARENT.cid
END
GO
if exists (select * from dbo.sysobjects where id =
object_id(N'dbo.trg_forumdata_update') AND OBJECTPROPERTY(id, N'IsTrigger') = 1)
DROP TRIGGER dbo.trg_forumdata_update
GO
-- Триггер, который срабатывает при UPDATE'е таблицы
-- (заново вычисляет lvl, tid и hierarchy)
CREATE TRIGGER trg_forumdata_update ON forum_forumdata FOR UPDATE
AS
IF UPDATE(cid)
BEGIN
RAISERROR('Номер записи менять нельзя!', 16, 1)
ROLLBACK TRAN
END
ELSE
IF UPDATE(pid)
BEGIN
UPDATE CHILD
SET lvl = CHILD.lvl - INS.lvl +
CASE
WHEN INS.pid IS NULL THEN 0
ELSE PARENT.lvl + 1
END
, tid =
CASE
WHEN (CHILD.pid IS NULL) OR (CHILD.pid = 0) THEN CHILD.cid
ELSE PARENT.tid
END
, hierarchy = ISNULL(PARENT.hierarchy, '.') +
dbo.zerofill(
CAST(INS.cid AS varchar(10))
) + '.' +
dbo.zerofill(
right(CHILD.hierarchy, len(CHILD.hierarchy) - len(INS.hierarchy))
)
FROM forum_forumdata AS CHILD JOIN INSERTED AS INS
ON CHILD.hierarchy LIKE INS.hierarchy + '%'
LEFT OUTER JOIN forum_forumdata AS PARENT
ON INS.pid = PARENT.cid
END
GO
TRUNCATE TABLE dbo.forum_forumdata
GO
IF (IDENT_SEED('dbo.forum_forumdata') IS NOT NULL )
SET IDENTITY_INSERT dbo.forum_forumdata ON
SET NOCOUNT ON
-- Все добавляемые в дерево реплики будут детьми этой
-- корневой реплики с номером 0 и pid = NULL
INSERT INTO forum_forumdata (cid, pid, subject, postdate, author, cookie, ipaddr,
email, message, subscribe) VALUES(0, NULL, 'Root item', '', '', '', '', '', '', '')
GO
-- И ещё немного записей для примера:
IF (IDENT_SEED('dbo.forum_forumdata') IS NOT NULL )
SET IDENTITY_INSERT dbo.forum_forumdata ON
INSERT INTO forum_forumdata (cid, pid, subject, postdate, author, cookie, ipaddr,
email, message, subscribe) VALUES(1, 0, 'Содержимое этого сервера', '23-07-1999 16:52:55',
'Webmaster', '', '', 'webmaster@gfk.ru', ' Интересует ваше мнение о содержимом. ', 'no')
INSERT INTO forum_forumdata (cid, pid, subject, postdate, author, cookie, ipaddr,
email, message, subscribe) VALUES(4, 0, 'Метки для 5-и значной шкалы', '01-07-1999 09:42:46',
'Давид', '', '', '', ' Я часто приобретаю исследования от различных компаний и я все время
спорю с ними относительно того, должна ли 5-и значная шкала Лайкерат иметь текстовые метки.
Я привожу аргумент, что если шкала не имеет меток, то респонденты отвечают в соответствии с
их собственным индивидуальным восприятием того, что такое 4 или 5. Мне интересны ваши комментари
по данному вопросу. ', 'no')
INSERT INTO forum_forumdata (cid, pid, subject, postdate, author, cookie, ipaddr, email,
message, subscribe) VALUES(5, 4, 'Re: Метки для 5-и значной шкалы', '01-07-1999 09:45:59',
'Жора', '', '', '', ' Это очень уместный вопрос, который конечно же необходимо обсудить.
Один из аспектов затрагивает проблему того, необходимо ли привязывать среднюю точку к тексту
типа ''ни согласен ни не согласен'' или ''равнодушен''. Альтернатива заключается в использовании
непрерывной шкалы, где ''1'' ассоциируется с наименьшим значением, а ''5'' -- с наивысшим.
В этом случае ''3'' не ассоциируется с ''равнодушием'', и наоборот. Тут же возникают проблемы
сравнения средних значений для разных шкал, а также относительное достоинство указываемых баллов
в вечных вопросах ''how high is up?'' которые позникают в опросах, посвященных удовлетворенности
пользователей, и т.д. Если вы используете статистики для анализа шкалированных данных, помните,
что статистические инструменты как правило предполагают, что данные взяты из непрерывных,
бесконечных и нормально распределенных переменных. ', 'no')
GO
SET NOCOUNT OFF
IF (IDENT_SEED('dbo.forum_forumdata') IS NOT NULL ) SET IDENTITY_INSERT dbo.forum_forumdata OFF
GO