Советы по схеме базы данных - PullRequest
0 голосов
/ 08 декабря 2010

У меня есть база данных, которая отслеживает скачки в Великобритании.

Раса содержит всю информацию для конкретной расы.

CREATE TABLE "race" (
  "id" INTEGER PRIMARY KEY AUTOINCREMENT,
  "date" TEXT NOT NULL,
  "time" TEXT NOT NULL,
  "name" TEXT NOT NULL,
  "class" INTEGER NOT NULL,
  "distance" INTEGER NOT NULL,
  "extra" TEXT NOT NULL,
  "going" TEXT NOT NULL,
  "handicap" INTEGER NOT NULL,
  "prize" REAL,
  "purse" REAL,
  "surface" TEXT NOT NULL,
  "type" TEXT NOT NULL,
  "course_id" INTEGER NOT NULL,
  "betfair_path" TEXT NOT NULL UNIQUE,
  "racingpost_id" INTEGER NOT NULL UNIQUE,
  UNIQUE("betfair_path", "racingpost_id")
);

В гонке может быть много записей.

CREATE TABLE "entry" (
  "id" INTEGER PRIMARY KEY AUTOINCREMENT,
  "weight" INTEGER,
  "allowance" INTEGER,
  "horse_id" INTEGER NOT NULL,
  "jockey_id" INTEGER,
  "trainer_id" INTEGER,
  "race_id" INTEGER NOT NULL,
  UNIQUE("race_id", "horse_id")
);

Запись может иметь 0 или 1 бегуна. При этом учитываются не участвующие в забеге лошади, которые участвовали в гонке, но которые не смогли стартовать.

CREATE TABLE "runner" (
    "id" INTEGER PRIMARY KEY AUTOINCREMENT,
    "position" TEXT NOT NULL,
    "beaten" INTEGER,
    "isp" REAL NOT NULL,
    "bsp" REAL,
    "place" REAL,
  "over_weight" INTEGER,
    "entry_id" INTEGER NOT NULL UNIQUE
);

Мой вопрос

Действительно ли это лучший способ сохранить мои данные Entry vs Runner? Примечание. Входные данные всегда собираются за один раз, а бегунок (в основном результат) обнаруживается позже.

  • Какой запрос мне понадобится, чтобы быстро найти общее количество записей и общее количество участников за определенную гонку.
  • Как я могу легко сопоставить информацию бегуна с информацией о входе без множественного выбора?

Приносим извинения, если я упустил что-то очевидное, но теперь я не в себе от написания этого приложения.

Ответы [ 3 ]

3 голосов
/ 08 декабря 2010

Ваша схема выглядит разумно. Ключевой конструкцией, используемой для решения ваших вопросов SQL, является LEFT JOIN, например:

SELECT COUNT(entry.id) entry_count, COUNT(runner.id) runner_count 
FROM entry
LEFT JOIN runner ON runner.entry_id = entry.id
WHERE race_id = 1

Из Википедии:

... внешнее левое соединение возвращает все значения из левой таблицы, а также совпадающие значения из правой таблицы (или NULL в случае отсутствия соответствующего предиката соединения).

В общем, для вашей схемы, при необходимости, сосредоточьтесь на таблице entry и LEFT JOIN на таблице runner.

1 голос
/ 11 декабря 2010

Тег реляционной базы данных, и вам нужен совет по вашей схеме в соответствии с заголовком. Даже если на один и тот же вопрос дан ответ, завтра у вас может быть больше.

Я не мог понять ваши три плоских файла, поэтому я собрал их в то, как они могут выглядеть в ▶ Реляционной базе данных ◀ , где информация организованы и запросы просты. Исчезновение мозга не редкость, когда информация остается в ее сложной форме.

Если вы еще не видели Стандарт реляционного моделирования, вам может понадобиться IDEF1X нотация .

Примечание, OwnerId, JockeyId и TrainerId - все PersonIds. Бесполезно производить новые, когда в столе уже сидит совершенно хороший уникальный. Просто переименуйте его, чтобы отразить его роль и PK таблицы, в которой он находится (актуальность этого станет ясна при кодировании).

Несколько SELECTS - ничего страшного, SQL - громоздкий язык, но это все, что у нас есть. Проблема:

  • сложность (необходимая из-за плохой модели) каждого SELECT

  • и узнаете ли вы и понимаете, как использовать подзапросы или нет.

    • Одноуровневые запросы, очевидно, очень ограничены и приведут к процедурной (построчно) обработке вместо обработки множеств.

    • Одноуровневые запросы приводят к огромным результирующим наборам, которые затем приходится разбивать на отправку с использованием GROUP BY и т. Д. Плохо для производительности, разбивая все эти нежелательные данные; лучше получить только те данные, которые вам действительно нужны.

Теперь запросы.

  1. Когда вы печатаете формы гонки, я думаю, вам понадобится Position, запланированный и объявленный для RaceEntry; это не элемент Runner.

  2. Теперь, когда мы избавились от тех Ids повсюду, которые вызывают всевозможные ненужные объединения, мы можем присоединиться непосредственно к соответствующим родителям (меньше объединений). Например. для формы гонки, которая относится только к RaceEntry, для Владельца вы можете присоединиться непосредственно к Person, используя WHERE OwnerId = Person.PersonId; нет необходимости присоединяться HorseRegistered или Owner.

  3. Соединения LEFT и RIGHT - это соединения OUTER, что означает, что строки на одной стороне могут отсутствовать. На этот метод был получен ответ, и вы получите Null, который вам придется обрабатывать позже (больше кода и циклов). Я не думаю, что это то, что вы хотите, если вы заполняете формы или веб-страницу.

  4. Идея здесь в том, чтобы думать, это термины Реляционные множества, а не построчная обработка. Но вам нужна база данных для этого. Теперь, когда у нас есть немного реляционной силы в звере, вы можете попробовать это для результата гонки (а не формы гонки) вместо процедурной обработки. Это скалярные подзапросы. Для переданных Race идентификаторов (внешний запрос касается только Race):

    <code>    SELECT  (SELECT ISNULL(Place, " ")
                FROM  Runner 
                WHERE RacecourseCode = RE.RacecourseCode
                AND   RaceDate       = RE.RaceDate
                AND   RaceNo         = RE.RaceNo
                AND   HorseId        = RE.HorseId) AS Finish,
            (SELECT ISNULL(Name, "SCRATCH")
                FROM  Runner R,
                      Horse  H
                WHERE R.RacecourseCode = RE.RacecourseCode
                AND   R.RaceDate       = RE.RaceDate
                AND   R.RaceNo         = RE.RaceNo
                AND   R.HorseId        = RE.HorseId
                AND   H.HorseId        = RE.HorseId) AS Horse,
            -- Details,
            (SELECT Name FROM Person WHERE PersonId = RE.TrainerId) AS Trainer,
            (SELECT Name FROM Person WHERE PersonId = RE.JockeyId) AS Jockey,
            ISP AS SP,
            Weight AS Wt
        FROM  RaceEntry RE
        WHERE RaceDate       = @RaceDate
        AND   RacecourseCode = @RacecourseCode  -- to print entire race form, 
        AND   RaceNo         = @RaceNo          -- remove these 2 lines
        ORDER BY Position
0 голосов
/ 08 декабря 2010

Соответствует записям и участникам, участвующим в данной гонке.

SELECT E.*, R.*
FROM entry E LEFT JOIN runner R on R.entry_id = E.id
WHERE E.race_id = X

Если в записи нет участника, то все поля R. * являются пустымиВы можете сосчитать такие пустые поля, чтобы ответить на ваш первый запрос (или, возможно, проще вычесть)

...