1

これは非常に急速に複雑になり、データベースの設計に疑問を呈し始めています。アプリケーションの基本的な概念は次のとおりです。

  1. ユーザーアカウント
  2. 特徴
  3. アクセスレベル

そのため、ユーザーは機能ごとに異なるアクセスレベルを持っています。かなり基本的で一般的なアプリケーションだと思います。

スキーマ:

CREATE TABLE `user_accounts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_login` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `user_password` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
  `user_fname` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `user_lname` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `user_group` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'Default',
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_login` (`user_login`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;

INSERT INTO `user_accounts` VALUES(1, 'email@example.com', 'secret', 'Example', 'Name', 'Admin');
INSERT INTO `user_accounts` VALUES(2, 'john@example.com', 'secret', 'John', 'Doe', 'Trainer');
INSERT INTO `user_accounts` VALUES(3, 'jane@example.com', 'secret', 'Jane', 'Doe', 'Default');

CREATE TABLE `user_access_meta` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `type` (`type`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `user_access_meta` VALUES(1, 'type_1');
INSERT INTO `user_access_meta` VALUES(2, 'type_2');
INSERT INTO `user_access_meta` VALUES(3, 'type_3');
INSERT INTO `user_access_meta` VALUES(4, 'type_4');
INSERT INTO `user_access_meta` VALUES(5, 'type_5');
INSERT INTO `user_access_meta` VALUES(6, 'type_6');

CREATE TABLE `user_access_levels` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_login` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `type` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `level` int(1) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_login_2` (`user_login`,`type`),
  KEY `user_login` (`user_login`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;

INSERT INTO `user_access_levels` VALUES(1, 'email@example.com', 'type_1', 1);
INSERT INTO `user_access_levels` VALUES(2, 'email@example.com', 'type_2', 1);
INSERT INTO `user_access_levels` VALUES(3, 'email@example.com', 'type_3', 0);
INSERT INTO `user_access_levels` VALUES(4, 'email@example.com', 'type_5', 2);
INSERT INTO `user_access_levels` VALUES(5, 'john@example.com', 'type_2', 1);
INSERT INTO `user_access_levels` VALUES(6, 'john@example.com', 'type_3', 1);
INSERT INTO `user_access_levels` VALUES(7, 'john@example.com', 'type_5', 3);
INSERT INTO `user_access_levels` VALUES(8, 'jane@example.com', 'type_4', 1);

これらのテーブルには実際にはもっと多くのフィールドがあり、それらの間に外部キー制約がありますが、この例ではそれらを細かく分けています。また、他の目的で個別に使用されます。

これにより、1人のユーザーに対して3つのテーブルすべてを正常に結合できました。

SELECT
ua.`user_fname`,
uam.`type`,
ual.`level`
FROM `user_access_meta` uam
LEFT JOIN `user_access_levels` ual
ON ual.`user_login` = 'email@example.com'
AND uam.`type` = ual.`type`
JOIN `user_accounts` ua
ON ua.`user_login` = 'email@example.com';

出力:

| USER_FNAME |   TYPE |  LEVEL |
--------------------------------
|    Example | type_1 |      1 |
|    Example | type_2 |      1 |
|    Example | type_3 |      0 |
|    Example | type_4 | (null) |
|    Example | type_5 |      2 |
|    Example | type_6 | (null) |

これでも理想的ではありませんが、私が思いつくことができるのはそれだけであり、それはその目的を果たします。


今、私がする必要があるのは、アクセスレベルを含むすべてのユーザーを選択することです。次のようになります。

| USER_FNAME |  type_1 |  type_2 |  type_3 |  type_4 |  type_5 |  type_6 |
--------------------------------------------------------------------------
|    Example |       1 |       1 |       0 |  (null) |       2 |  (null) |
|       John |  (null) |       1 |       1 |  (null) |       3 |  (null) |
|       Jane |  (null) |  (null) |  (null) |       1 |  (null) |  (null) |

これは最高のデザインではなかったと思いますが、このデザインを採用した理由は、機能を簡単に追加および削除したり、個別に一時的に無効にしたりできるようにするためです。

デザインを再考する必要がありますか?このデザインで私が探している結果を得るのも可能ですか?

これをSQLFiddleに載せました。http://sqlfiddle.com/#!2/bb313/2/0

4

3 に答える 3

1

テーブルのデザインと、必要な形式でデータを取得する方法の両方について、いくつか提案があります。

最初にデータベースの設計について、私がアドバイスする変更は表にありますuser_access_levels。テーブルを次のように変更します。

CREATE TABLE `user_access_levels` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `type_id` int(11) NOT NULL,
  `level` int(1) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id_2` (`user_id`,`type_id`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;

とのみを格納できる場合は、このテーブルにuser_loginとを格納する必要はありません。これらの両方を、それぞれのテーブルへの外部キーとして使用します。typeuser_idtype_id

次に、必要な形式でデータを取得します。MySQL には関数がないため、集計関数を含むステートメントPIVOTを使用する必要があります。CASE

select ua.user_fname, 
  MIN(CASE WHEN uam.type = 'type_1' THEN ual.level END) type_1,
  MIN(CASE WHEN uam.type = 'type_2' THEN ual.level END) type_2,
  MIN(CASE WHEN uam.type = 'type_3' THEN ual.level END) type_3,
  MIN(CASE WHEN uam.type = 'type_4' THEN ual.level END) type_4,
  MIN(CASE WHEN uam.type = 'type_5' THEN ual.level END) type_5,
  MIN(CASE WHEN uam.type = 'type_6' THEN ual.level END) type_6
FROM user_accounts ua
LEFT JOIN user_access_levels ual
  ON ua.id = ual.user_id
LEFT JOIN user_access_meta uam
  ON ual.type_id = uam.id
group by ua.user_fname

デモで SQL Fiddle を見る

このバージョンは、値を取得するタイプの列が事前にわかっている場合に機能します。ただし、不明な場合は、準備済みステートメントを使用してこれを動的に生成できます。

以下は、準備済みステートメントを使用したバージョンのクエリです。

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MIN(case when type = ''',
      type,
      ''' then level end) AS ',
      replace(type, ' ', '')
    )
  ) INTO @sql
FROM user_access_meta;

SET @sql = CONCAT('SELECT ua.user_fname, ', @sql, ' FROM user_accounts ua
LEFT JOIN user_access_levels ual
  ON ua.id = ual.user_id
LEFT JOIN user_access_meta uam
  ON ual.type_id = uam.id
group by ua.user_fname');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

デモで SQL Fiddle を見る

于 2012-08-17T18:01:10.610 に答える
0

私は MySQL の詳細に精通していませんが、ピボット テーブル クエリのかなり基本的な例を説明しているように思えます。あなたが探しているものは私には合理的だと思われるので、ここで示したことに基づいて、データモデルを再検討することについてあまり心配することはないと思います. 古い見た「ヒットが痛むまで正規化、機能するまで非正規化:)」に基づいて、「レベル」を「タイプ」テーブルに戻すことに気付くかもしれません:)

ちょうど私の$ 0.02。幸運を。

于 2012-08-06T23:17:34.073 に答える
0

通常、BIGINT 列を使用し、ビット マスキングを使用して値を設定します。

たとえば、レベル 1 = 2、レベル 2 = 4、レベル 3 = 8、レベル 4 = 16 などです。

レベル 1 とレベル 2 のアクセス権を誰かに与える:

update user set access_level = 2 & 4

誰かがレベル2アクセスを持っていますか?

select 1 from user where access_level | 2 AND user_id = ?
于 2012-08-06T23:38:50.553 に答える