20210725のMySQLに関する記事は11件です。

CROSS JOIN(クロス結合)を通したINNER JOINの考え方

※この記事は実務半年〜1年の方に向けた記事です。 SQLの結合を書く際、なかなか思い通りの結果が取得できずに結合条件に悩むことがあります。 ただ、とある現場でクロス結合を使えばだいぶ楽になることを学んだので、今回はその考え方を共有してきます。 まず、今回使用するテーブルは次の通りです。 これを単純にクロス結合します。クロス結合とは、総当たりで結合するということです。 SELECT * FROM restaurant_name,restaurant_reputaion こうなります。これでも総当りの意味がいまいちつかめない人のために一部とりだすと、こういうことです(僕は最初そうでした) つまり、 ①あくせる亭という一行に対して評判テーブルがすべてくっつき、 ②総当たりのためあくせる亭が評判テーブルの行数分増えている ということです。あとは同じことを左側のテーブル(ここではレストランテーブル)の行数だけ繰り返しているだけです。 ここまででクロス結合の考え方はつかめたはずです。 次に、クロス結合にある条件を付け足します。 SELECT * FROM restaurant_name,restaurant_reputaion WHERE restaurant_name.id = restaurant_reputaion.id 日本語にすると「レストランテーブルのidと評判テーブルのidが等しいものをくっつける」といったところでしょうか。 これを先程の画像を用いて説明します。 まず先程同様あくせる亭に評判テーブルがくっつきます。 続いてwhere句をみると、「レストランテーブルのidと評判テーブルのidが等しいものをくっつける」ので、 id=1以外の行はくっつかずに消滅します。あとはレストランテーブルの行数分これを繰り返していきます。 すると結果が、 となります。 今回はもう結果は貼りませんが、これはINNER JOINを使用したSQL文の SELECT * FROM restaurant_name INNER JOIN restaurant_reputaion ON restaurant_name.id = restaurant_reputaion.id と同じ結果になります。 INNER JOINでの結合条件が思いつかないときは、まずクロス結合で結果を表示してから、 絞っていくとわかりやすいかと思うのでぜひ試してみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】Laravel + Docker + RDS(MySQL) でデプロイした時のDB関連のエラーとその解決

はじめに AWS(EC2)にデプロイする際、DB関連のエラーで躓いたため記録として残します。 DB関連にフォーカスしているので、それ以外のインストールの手順は省いています。 前提 ・今からmigrateします ・EC2インスタンス作成済み ・sshログインできる 条件 macOS: "11.2.3 Big Sur" php artisan -V # > Laravel Framework 6.20.30 php -v # > PHP 8.0.8 nginx -v # > nginx version: nginx/1.12.2 mysql -h エンドポイント -u マスターユーザー名 -p データベース名 Enter password: # > Server version: 8.0.25 git --version # > git version 2.32.0 1. php artisan migrateでエラー MySQLをインストール ssh sudo yum -y install mysql artisanコマンドが使えるディレクトリに移動してmigrate php artisan migrate すると以下のエラーが... SQLSTATE[HY000] [2002] Connection timed out DBに接続をしようとしたけどできてない様子。 .envを確認して何回か修正したが、改善せず。 .envファイル 入れる値 DB_HOST エンドポイント DB_USERNAME マスターユーザー名 DB_PASSWORD マスターパスワード 解決 AWSのセキュリティグループ→インバウンドルールを確認すると、TypeがMySQLではなかったのが原因でした。 →MySQLに変更することで無事migrateすることができました。 php artisan migrate # migrate成功 Migration table created successfully. 念の為テーブル確認 ターミナル mysql -h エンドポイント -u マスターユーザー名 -p データベースの指定 -p の後は空白を開ける。空白を開けずに入力するとパスワードとなってしまいます。 以下が表示されれば接続成功。 ターミナル Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 174 Server version: 8.0.25 Source distribution Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]> もし、-pを指定せずに接続しても以下のように指定することで見ることができます。 show databases;とすることで、全てのdatabaseが表示。 ターミナル MySQL [(none)]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | lantern ← このdetabaseを確認したい | mysql | | performance_schema | | sys | +--------------------+ lanternというdetabaseを見たかったらuseで指定する。 ターミナル MySQL [(none)]> use lantern Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed MySQL [lantern]> ← lanternに変更された 参考 このエラーを解決するにあたって以下のサイトを参考にさせて頂きました。 2. php artisan db:seedでエラー Class Faker\Factory Faker\Factoryのクラスがないよと言われる。 解決 Herokuで同じようなことが起こっている人たちがいましたが、AWSでも同じようにすると解決できました。 結論から言うと、以下のようにfakerphp/fakerをrequire-devからrequireへ移動しました。 composer.json "require": { "php": "^7.2.5|^8.0", "doctrine/dbal": "^3.1", "fideloper/proxy": "^4.4", "intervention/image": "2.5.1", "laravel/framework": "^6.20.26", "laravel/tinker": "^2.5", + "fakerphp/faker": "^1.9.1" }, "require-dev": { "barryvdh/laravel-debugbar": "^3.6", "facade/ignition": "^1.16.15", "laravel/ui": "^1.0", "mockery/mockery": "^1.0", "nunomaduro/collision": "^3.0", "phpunit/phpunit": "^8.5.8|^9.3.3" - "fakerphp/faker": "^1.9.1" }, デプロイの際、パッケージのインストールで以下のように記述しました。 composer install --no-dev --prefer-dist --no-devとすることで、「開発環境のみ必要で本番環境で必要ないパッケージはインストールしない」とできます。 具体的には、phpunitやdebugbarなどは本番環境には必要ないためインストールされないようにしています。 「開発環境のみ」というのはrequire-devの部分です。 上記のコマンドを実行するとrequire-devで書かれているものはインストールされません。 修正後、gitにpushし本番環境でもcomposerをアップデートして反映させます。 composer update この後にもう一度php artisan db:seedをすると無事実行されました。 php artisan db:seed Database seeding completed successfully. 参考 このエラーを解決するにあたって以下のサイトを参考にさせて頂きました。 おわり この他にも、ディレクトリの記述ミスで実行できなかったのもあり、これを機にディレクトリをしっかり意識するようになりました。 初歩的なミスばかりですが、おかげでデプロイの概要が少しずつ理解できてきたかなと感じます! このまま自動デプロイ目指してやっていきます! 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SQLのCASE式を使って集計する場合のテクニック(1)

ポイント CASE式は、単純にラベルを読み替えているだけではない 既存のコード体系を新しい体系に変換して集計することができる 例えば、以下のような都道府県テーブルと、市区町村テーブルがあるが 地方区分、つまり東北地方や東海地方ごとの(人口などの)件数を出したい場合 リージョンズ(regions)というモデルを新設するのは手間 集計だけが目的ならその選択肢はない 都道府県や市区町村ごとではなく地方ごとに集計したい場合 地方 件数 中国 10 中部 11 九州 20 北海道 60 四国 92 東北 176 近畿 1700 関東 2001 こんなとき case式が役に立ちます 実例 SELECT CASE p.name WHEN '北海道' THEN '北海道' WHEN '青森県' THEN '東北' WHEN '岩手県' THEN '東北' WHEN '宮城県' THEN '東北' WHEN '秋田県' THEN '東北' WHEN '山形県' THEN '東北' WHEN '福島県' THEN '東北' WHEN '東京都' THEN '関東' WHEN '茨城県' THEN '関東' WHEN '栃木県' THEN '関東' WHEN '群馬県' THEN '関東' WHEN '埼玉県' THEN '関東' WHEN '千葉県' THEN '関東' WHEN '神奈川県' THEN '関東' WHEN '新潟県' THEN '中部' WHEN '富山県' THEN '中部' WHEN '石川県' THEN '中部' WHEN '福井県' THEN '中部' WHEN '山梨県' THEN '中部' WHEN '長野県' THEN '中部' WHEN '岐阜県' THEN '中部' WHEN '静岡県' THEN '中部' WHEN '愛知県' THEN '中部' WHEN '京都府' THEN '近畿' WHEN '大阪府' THEN '近畿' WHEN '三重県' THEN '近畿' WHEN '兵庫県' THEN '近畿' WHEN '滋賀県' THEN '近畿' WHEN '奈良県' THEN '近畿' WHEN '和歌山県' THEN '近畿' WHEN '鳥取県' THEN '中国' WHEN '島根県' THEN '中国' WHEN '岡山県' THEN '中国' WHEN '広島県' THEN '中国' WHEN '山口県' THEN '中国' WHEN '徳島県' THEN '四国' WHEN '香川県' THEN '四国' WHEN '愛媛県' THEN '四国' WHEN '高知県' THEN '四国' WHEN '福岡県' THEN '九州' WHEN '佐賀県' THEN '九州' WHEN '長崎県' THEN '九州' WHEN '大分県' THEN '九州' WHEN '熊本県' THEN '九州' WHEN '宮崎県' THEN '九州' WHEN '鹿児島県' THEN '九州' WHEN '沖縄県' THEN '九州' ELSE NULL -- ELSE は必ず書く END AS region, -- END 忘れずに count(s.id) FROM prefectures AS p -- 色んなテーブルを紐づけていく LEFT JOIN cities AS c ON c.prefecture_id = p.id LEFT JOIN shops AS s ON s.city_id = c.id -- グループ化 GROUP BY region -- AS つけておくと 2箇所に同じ case文を書く手間がない 注意点 分岐が返すデータ型は統一する エラーにならないので自分で気を付ける ENDの書き忘れをしないように。しても構文エラーになる。 ELSEは必ず書く 書かなくてもエラーにはならない、なぜなら暗黙的に ELSE NULL 扱いになるから 「エラーにはならないけど結果が違う」原因を探すのに時間かかる可能性があるから 明示的にNULLを返すようにした方が気づきやすい 参照 1-7p
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Databricksにて外部Hive Metastoreを構築する方法の検証

注意事項 調査中ですが、意見を募集したいため記事を投稿します。 記事内容を更新する可能性があります。 概要 Databricksにて外部Hive Metastoreを構築する方法の検証を整理します。 下記の表が外部Hive Metastoreを構築する方法の概要、それぞれの検証結果の詳細を後述します。 # 方法 実施可否 問題・課題 1 Databricksのドキュメント通りにSpark Configにより構築する方法 〇 spark.conf.getにより接続情報を取得できてしまうこと。 2 Hive Metastoreへの接続情報ファイル(hive-site.xml)を置き換える方法 × ファイル置き換え前のデータベースに接続してしまうこと。 3 Hive Metastoreへの接続情報ファイルに対する参照パスの環境変数(CONF_HIVE_CONF_DIR)を置き換える方法 × 外部メタストアデータベースへ接続できず、データベースを作成しても永続化されないこと。 Databricksではデフォルトで内部のHive Metastoreデータベースを参照する仕様であり、Azure Databricksではリージョンごとに共通のAzure Database for Mysqlを利用するようになっているようです。Hive Metastoreデータベースへの接続情報の取得方法としては下記のドキュメントで紹介されております。 引用元:Azure Purview で Hive メタストア データベースを登録してスキャンを設定する - Azure Purview | Microsoft Docs 参考になる情報としてDatabricksのドキュメントがありますが、AWS版DatabrickstとAzure Databricksでは記載内容が異なり、どちらも参考になります。 外部Apache Hiveメタストア - Azure Databricks - Workspace | Microsoft Docs External Apache Hive metastore | Databricks on AWS SparkにおけるHive Metastoreの動作を概要を利用するには、GMOインターネットグループ様のブログ記事がとても参考になります。 Hadoop上でSpark+Delta Lakeを検証してみた~続き | GMOインターネットグループ 次世代システム研究室 1. Databricksのドキュメント通りにSpark Configにより構築する方法 構築方法 下記の記事に記載しております。 Databricksにおける外部Hive MetastoreをMySQL(Azure Database for MySQL)にてローカルモードでSpark Configにて構築する方法 - Qiita Databricksにおける外部Hive MetastoreをAzure SQL DatabaseにてローカルモードでSpark Configにて構築する方法 - Qiita 課題 課題1-1. spark.conf.getにより接続情報を取得できてしまうこと Databricksシークレットに格納することで値を表示しないようにできてしまうが、jdbc経由でHive Metastoreデータベースに接続できてしまうという課題がある。 2. Hive Metastoreへの接続情報ファイル(hive-site.xml)を置き換える方法 構築方法 下記のコードを実行してhive-site.xmlを置き換えます。 contents = """ <configuration> <property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:sqlserver://{databaseへの接続情報}</value> <description>JDBC connect string for a JDBC metastore</description> </property> <property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.microsoft.sqlserver.jdbc.SQLServerDriver</value> <description>Driver class name for a JDBC metastore</description> </property> <property> <name>javax.jdo.option.ConnectionUserName</name> <value>{ユーザー}</value> <description>Username to use against metastore database</description> </property> <property> <name>javax.jdo.option.ConnectionPassword</name> <value>{パスワード}</value> <description>Password to use against metastore database</description> </property> <!-- If the following two properties are not set correctly, the metastore will attempt to initialize its schema upon startup --> <property> <name>datanucleus.schema.autoCreateAll</name> <value>false</value> </property> <property> <name>datanucleus.autoCreateSchema</name> <value>false</value> </property> <property> <name>datanucleus.fixedDatastore</name> <value>true</value> </property> <property> <name>datanucleus.connectionPool.minPoolSize</name> <value>0</value> </property> <property> <name>datanucleus.connectionPool.initialPoolSize</name> <value>0</value> </property> <property> <name>datanucleus.connectionPool.maxPoolSize</name> <value>1</value> </property> <property> <name>hive.stats.autogather</name> <value>false</value> </property> <property> <name>mapred.reduce.tasks</name> <value>100</value> </property> <!-- To mitigate the problem of PROD-4498 and per HIVE-7140, we need to bump the timeout. Since the default value of this property used by Impala is 3600 seconds, we will use this value for actual deployment (http://www.cloudera.com/content/cloudera/en/documentation/core/latest/topics/cm_props_cdh530_impala.html). --> <property> <name>hive.metastore.client.socket.timeout</name> <value>3600</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/local_disk0/tmp</value> </property> <property> <name>hive.metastore.client.connect.retry.delay</name> <value>10</value> </property> <property> <name>hive.metastore.failure.retries</name> <value>30</value> </property> </configuration> """ dbutils.fs.put( file = "file:/databricks/hive/conf/hive-site.xml", contents = contents, overwrite = True, ) 課題 課題2-1. ファイル置き換え前のデータベースに接続してしまうこと ファイルを置き換えてもデータベースは切り替わらず、クラスターを再起動するとhive-site.xmlが初期化されてしまいます。 hive-site.xml置き換え前のデータベース hive-site.xml置き換え後のデータベース 3. Hive Metastoreへの接続情報ファイルに対する参照パスの環境変数(CONF_HIVE_CONF_DIR)を置き換える方法 構築方法 下記のコードにより、dbfs上にhive-site.xmlを配置します。。 contents = """ <configuration> <property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:sqlserver://{databaseへの接続情報}</value> <description>JDBC connect string for a JDBC metastore</description> </property> <property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.microsoft.sqlserver.jdbc.SQLServerDriver</value> <description>Driver class name for a JDBC metastore</description> </property> <property> <name>javax.jdo.option.ConnectionUserName</name> <value>{ユーザーID}</value> <description>Username to use against metastore database</description> </property> <property> <name>javax.jdo.option.ConnectionPassword</name> <value>{パスワード}</value> <description>Password to use against metastore database</description> </property> <!-- If the following two properties are not set correctly, the metastore will attempt to initialize its schema upon startup --> <property> <name>datanucleus.schema.autoCreateAll</name> <value>false</value> </property> <property> <name>datanucleus.autoCreateSchema</name> <value>false</value> </property> <property> <name>datanucleus.fixedDatastore</name> <value>true</value> </property> <property> <name>datanucleus.connectionPool.minPoolSize</name> <value>0</value> </property> <property> <name>datanucleus.connectionPool.initialPoolSize</name> <value>0</value> </property> <property> <name>datanucleus.connectionPool.maxPoolSize</name> <value>1</value> </property> <property> <name>hive.stats.autogather</name> <value>false</value> </property> <property> <name>mapred.reduce.tasks</name> <value>100</value> </property> <!-- To mitigate the problem of PROD-4498 and per HIVE-7140, we need to bump the timeout. Since the default value of this property used by Impala is 3600 seconds, we will use this value for actual deployment (http://www.cloudera.com/content/cloudera/en/documentation/core/latest/topics/cm_props_cdh530_impala.html). --> <property> <name>hive.metastore.client.socket.timeout</name> <value>3600</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/local_disk0/tmp</value> </property> <property> <name>hive.metastore.client.connect.retry.delay</name> <value>10</value> </property> <property> <name>hive.metastore.failure.retries</name> <value>30</value> </property> </configuration> """ dbutils.fs.put( file = "dbfs:/FileStore/hive/conf/hive-site.xml", contents = contents, overwrite = True, ) Environment Variablesにて下記の値を設定。 CONF_HIVE_CONF_DIR=/dbfs/FileStore/hive/conf/hive-site.xml 課題 課題3-1. 外部メタストアデータベースへ接続できず、データベースを作成しても永続化されないこと データベース作成することは可能ですが、Hive Metastoreデータベースにもデータが書き込まれておりませんでした。 クラスターを再起動すると、データベースが初期化されてしまいます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsプロジェクトのデータベースの構造のER図をPlantUMLで自動的に吐き出せるようにしてみた

モチベーション QiitaでもPlantUMLを表示できるようになってきたので、いよいよPlantUMLが民主化されてきました。 そこで「データベースの情報を基に自動的にER図作ってくれたらいいのになぁ...」と思ったので勢いで作ってみました。 対象となる技術の選定と制限 ER図の特徴の都合上、各種リレーションの定義を取得するためにはORMが使用されているものを対象に作成する必要があります。 今回は以下が使用されている環境を想定してスクリプトを作成・実行しました MySQL Rails これは実行テストを行った対象である 「ハッカソンの開催情報を自動でお知らせするBot HackathonPortal 」 の実行環境に合わせたためです。 同じ Rails でも Postgresql や SQLite においては実行確認していないのでうまくいくかわかりません。(試してみてうまくいったご報告をいただけましたら幸いです。その時は更新いたします) 実際に作成したソースコード 以下が実際に作成したコードになります task export_entity_relationship_diagram_plantuml: :environment do # 使用されている全てのテーブルのModelの情報を取得するために全て読み込む Rails.application.eager_load! # 使用されている全てのテーブルを持っていて関係性がわかるModelの情報を取得する model_classes = ActiveRecord::Base.descendants.select{|m| m.table_name.present? } class_name_model_class_pair = model_classes.index_by(&:to_s) relation_entity_components = Set.new entity_component_fields = Set.new foreign_key_pairs = {} # ER図においてそれぞれのエンティティとの関連性を記述していく class_name_model_class_pair.values.each do |model_class| model_class.reflections.values.each do |relation_info| # polymorphic の belongs_to の構造はリレーション関係がわからないのでスルー next if relation_info.polymorphic? # belongs_to 参照元を取得する場合はfrom と toの対象を交換する if relation_info.instance_of?(ActiveRecord::Reflection::BelongsToReflection) to_model_class = model_class from_model_class = class_name_model_class_pair[relation_info.class_name] else from_model_class = model_class to_model_class = class_name_model_class_pair[relation_info.class_name] end primary_keys = [from_model_class.primary_key].flatten to_foreign_key_string = [to_model_class.table_name, relation_info.foreign_key].join(".") if relation_info.options[:primary_key].present? from_foreign_key_string = [from_model_class.table_name, relation_info.options[:primary_key]].join(".") else from_foreign_key_string = primary_keys.map{ |primary_key| [from_model_class.table_name, primary_key].join(".") }.join(",") end # 外部キーのカラムとの関係性を記録する foreign_key_pairs[to_foreign_key_string] = from_foreign_key_string # has_many 関係性を表現 1対多の場合 if relation_info.instance_of?(ActiveRecord::Reflection::HasManyReflection) # 0 ~ 複数 relation_entity_components << [from_model_class.table_name, "--o{", to_model_class.table_name].join(" ") # has_one 関係性を表現 1対1の場合 elsif relation_info.instance_of?(ActiveRecord::Reflection::HasOneReflection) # belongs_toの要素が先に登録されていたら消す relation_entity_components.delete([from_model_class.table_name, "--o{", to_model_class.table_name].join(" ")) # 0 ~ 1 relation_entity_components << [from_model_class.table_name, "|o--o|", to_model_class.table_name].join(" ") # has_many :through 関係性を表現 多対多の場合 elsif relation_info.instance_of?(ActiveRecord::Reflection::ThroughReflection) relation_entity_components << [from_model_class.table_name, "}o--o{", to_model_class.table_name].join(" ") # belongs_to 参照元を取得 とりあえず 1対多として記録 elsif relation_info.instance_of?(ActiveRecord::Reflection::BelongsToReflection) # has_one の要素が記録されていたらスキップ unless relation_entity_components.include?([from_model_class.table_name, "|o--o|", to_model_class.table_name].join(" ")) relation_entity_components << [from_model_class.table_name, "--o{", to_model_class.table_name].join(" ") end end end end # ER図においてそれぞれのエンティティのカラムの特徴を記述していく class_name_model_class_pair.values.each do |model_class| primary_keys = [model_class.primary_key].flatten entity_components = [] entity_components << ['entity', '"' + model_class.table_name + '"', '{'].join(" ") model_class.columns.each do |model_column| table_column_string = [model_class.table_name , model_column.name].join(".") if primary_keys.include?(model_column.name) entity_components << ["+", model_column.name, "[PK]", model_column.sql_type].join(" ") entity_components << "==" # 外部キーには目印 elsif foreign_key_pairs[table_column_string].present? entity_components << ["#", model_column.name, "[FK(" + foreign_key_pairs[table_column_string] + ")]", model_column.sql_type].join(" ") else entity_components << [model_column.name, model_column.sql_type].join(" ") end end entity_components << '}' entity_components << "\n" entity_component_fields << entity_components.join("\n") end # PlantUMLを記述 plntuml_components = Set.new plntuml_components << "```plantuml" plntuml_components << "@startuml" plntuml_components += entity_component_fields plntuml_components += relation_entity_components plntuml_components << "@enduml" plntuml_components << "```" export_plantuml_path = Rails.root.join("er-diagram.plantuml") File.write(export_plantuml_path, plntuml_components.to_a.join("\n")) end 上記のソースコードを rails runner または rake task名, rails task名 にて実行することで、プロジェクト内のER図を表したPlantUMLファイルが出力されます 実行テスト 上記のスクリプトを実行して のプロジェクトのER図を出力してみた内容が以下の通りになります ER図を輩出してみたことで、使用していないテーブルやポリモーフィックの多様などデータ構造におけるアンチパターンを多用していることが判明したので、本プロジェクトについては後日テーブル構造を見直そうと思います... また HackathonPortal のプロジェクトやソースコードはこちらにて公開していますので興味がある方はこちらも参照してください。 処理の中身の解説などはこちらを参照してください 最新のハッカソンの開催情報を自動で集めて、お知らせするBotを作ったので頭の中を紹介 まだフォローしていない方はフォローのほどよろしくお願いします。 @HackathonPortal これから 希望が多くありましたら、汎用的に使用できるように gem化 しようと思います。 また勢いで作ったので汎用化にあたり漏れているユースケースがあるかもしれません。 その場合は都度更新していきたいと思います。 参考 PlantUMLでER図を書いて、githubで管理してみた やさしい図解で学ぶ ER図 表記法一覧
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MAMP環境でのLaravelとMySQLの接続エラー

今まではSQLiteを使用していましたが、今回はMySQLを使用することにしました。 しかし、開始早々接続エラーで苦戦したので、こちらに解決方法を記載しておきます。 環境 MAMP OS:macOS Laravel:8.51.0 PHP:7.4.12 MySQL:5.7.32 エラー画面 phpmyadminでDBを作り、いざ接続しようとしたらこのエラーが出ました。 「Connection refused」ということで、うまく接続できていないようです。 どうやら.envファイルとdatabase.phpファイルの記述が間違えているようでした。 MAMPの情報を確認する MAMPを起動した際に、こちらのページが表示されます。 こちらのページの下の方に「MySQL」という項目があるのでクリックします。 するとこちらの情報が出てきます。 このport、Username、Password、Socketがこのあと必要になるので、メモ等に控えておきます。 .envファイルの修正 修正前の記述がこちらです。 .env DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_USERNAME=root DB_PASSWORD= この中のDB_PORT、DB_USERNAME、DB_PASSWORDを先ほどメモしたものに変更します。 そして新たにDB_SOCKETの記述を追加します。 DB_DATABASEは自分で作成したデータベース名を入れます。 修正したものがこちらです。 .env DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=8889 DB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock DB_DATABASE=データベース名 DB_USERNAME=root DB_PASSWORD=root database.phpの修正 次にdatabase.phpを修正します。 database.phpはconfigファイル内にあります。 その中でmysqlの記載部分を探します。 修正前がこちら。 .database.php 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 第2引数部分を、メモした通りに変更します。 databaseは先ほど同様、自分で作成したデータベース名を入れます。 .database.php 'port' => env('DB_PORT', '8889'), 'database' => env('DB_DATABASE', 'データベース名'), 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', 'root'), 以上を変更して、再起動をしてから再接続したところ、無事DB接続することができました。 ちなみにポート番号を変更するには? 今回DBの接続するにあたり、ポートが初期値のままだったことに気づきました。 MAMPのデフォルトのポートはApacheが8888、MySQLが8889です。 変更するにはMAMPのPreferencesのこちらの画面で変更することができます。 一般的なポートの設定をするにはSetWeb&MySQL Ports toの「80 & 3306」を押します。 するとApacheが80、MySQLが3306に変わります。 そうすることで、ローカル環境で接続する際に、URLが「http://localhost:8888/」から「http://localhost/」に変わります。 ちなみにデフォルトの番号に戻したいときは「MAMP default」で元に戻ります。 ポート番号を変更した際は、上記の.envとdatabase.phpの記載の修正を忘れずに。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アプリ作成②「じゃんけんゲーム」

【作成の過程】 次に取り組んだのは、昔ゲームセンターで遊んだ「じゃんけんポンっ」のゲーム作成。 目指すイメージは決まっているが、そこまでどういうプロセスをたどるかは、完全にオリジナル。 とりあえず、基本的なアプリ設計過程と同じように、要件定義→設計→実装→テストというウォーターフォール型の行程をたどってみた。 要件定義は、ただじゃんけんで勝敗を決めるゲームを作るというごくごく単純なものなので難しくない。 設計では、機能設計で実装したい機能を列挙して、データベース設計、画面設計とアプリのイメージを細分化・具体化していった。 そして、実装を始めると、この画面があった方がいいとか課題が出てきて、当初の設計の甘さを感じた(笑) 最終的にテストをして、最低限の形にはなったかなという感じ。 まだまだ、デザインがチープだったり、実装したい機能(ルーレット、対戦モード、コイン複数枚がけ…)はあるが、今後の課題ということで、 まずは、オリジナルのアプリを作り、それを形にできたことのうれしさを感じた。 【アプリの概要】 小さい頃にゲームセンターで、わずかなコインを握りしめて白熱した「じゃんけんゲーム」をプログラミングで再現した。 勝った時は歓喜し、負けた時は絶望に打ちひしがれて、帰路についた。なぜ、あの頃はあんなに夢中になれたんだろう。皆さんが童心に帰ることができればいいですね。 【主な機能】 ・ユーザー登録機能 ・ユーザーログイン機能 ・画像ファイルアップロード機能 ・ゲーム機能 ・ランキング表示機能 【画面一覧】 ・ユーザー登録画面 ・ユーザー登録確認画面 ・ユーザー登録完了画面 ・ログイン画面 ・ゲーム画面 ・ゲーム結果画面 ・コイン獲得画面 ・ランキング画面 【開発環境】 ・CSSフレームワーク:Bootstrap ・言語:PHP ・DB:MySQL 【苦労した点】 ・ゲストモードの実装(どのような方法をとればいいか試行錯誤し、結果的にguestをデータベースに登録し、ゲストボタンを押した際、guestのデータを取得しアプリを使えるようにした。※上書きは許容しない) ・コイン枚数の画面間引継ぎ方法(セッションの受け渡しをできるだけシンプルなコードで表現したかったが…) 【今後搭載したい機能】 ・対戦モード(人間二人で使用) ・コンピューターの出し手や、獲得するコインを、ルーレットで決定させる。 ・2枚の3枚betや、勝ったコインを元手にチャレンジができ、ハイリスクハイリターンが狙える。 【アプリURL】 ・じゃんけんゲーム ・ソースコード
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アプリの作成①「メッセージ投稿アプリ」

【作成の過程】 今まで学習した基礎的な知識を基に、オリジナルアプリの作成を始めた。 まずは、PHPの章の学習で使ったUdemy教材をベースにデザインやゲストの実装等アレンジし、「メッセージ投稿アプリ」を作成した。 今までは、動画で提示される課題の通りのものを作っていたが、 少しでもオリジナル要素を加えようとすると、正解がないわけで、どうしたらいいのかいろいろと考え込んでしまったが、 ひとまず形にし、デモデータを搭載した。 【アプリの概要】 複数のユーザーがメッセージを投稿・共有できるアプリ 【主な機能】 ・ユーザー登録機能 ・ユーザーログイン機能 ・画像ファイルアップロード機能 ・メッセージ投稿機能 ・メッセージ返信機能 ・メッセージ一覧表示機能 ・メッセージ詳細表示機能 ・ページネーション機能 【画面一覧】 ・ユーザー登録画面 ・ユーザー登録確認画面 ・ユーザー登録完了画面 ・ログイン画面 ・メッセージ投稿・一覧表示画面 ・メッセージ詳細画面 【開発環境】 ・CSSフレームワーク:Bootstrap ・言語:PHP ・DB:MySQL 【今後搭載したい機能】 ・メッセージ修正機能 ・いいね機能 【アプリURL】 ・メッセージ投稿アプリ ・ソースコード
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPとSQLの理解

【PHP,MySQL】 Udemy動画教材の「PHP+MySQL(MariaDB) Webサーバーサイドプログラミング入門」(たにぐちまこと氏)で学習した。 これまでは、デザインを作る要素が強かったHTML,CSSに、PHP,MySQLが加わることで、繰返し、条件分岐やデータベースとの連携など、よりプログラミングらしくなってきて、実現できる世界が広がったし、世の中のWebサイトの仕組みがなんとなくわかってきて、自分でもサービスを作ってみたいと思った。 動画合計12時間の長丁場で、特に初期の知識が空の状態だと、知識がなかなか定着せずに、モチベーションを保つのが難しかったが、行き詰まったら飛ばすなど、工夫しながら進めた。 【動画学習のコツ】 全体的に言えることだが、動画を見ながらコーディングする際重要なのは、ゴール(目的)を明確に認識したうえで取り組むことだと思う。でなければ、ただ写しているだけの「作業」になってしまう。だから、その章・レッスンごとのゴールを確認したうえで、まずは動画を見ないで自分でできるところまで進め、つまづいたところをメモに残し、動画で答え合わせをするという方法がいいと思った。 でも、まずは、動画と一緒に作り、動かすことが大事だと思う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

データベースについて(リレーション、正規化、テーブル設計)

データベース入門 データベースとは サイトの顧客情報や商品情報などのデータを管理する場所 DBの種類 RDB MySQL 無料で使えて、実際の仕事現場でもよく使われている PostgreSQL Oracle どれを使っても基本的な役割は一緒 テーブル/カラム/レコード テーブル エクセルで言うところのシート データの種類でテーブルごとに管理をする カラム エクセルで言うところの列 レコード エクセルで言うところの行 SQLとは データベースはコマンドで操作する このデータベースを操作するコマンド(言語)をSQLという 追加、変更、削除など 例:select * from users limit 2 ; usersのテーブルから2件のデータを表示する 参考動画【データベース入門】 DBの設計#1 DBのリレーション 1:n(1:多) 会社情報 : 社員情報 一つの会社に複数の社員が入ることができる n:n(多:多) 社員情報 : サークル 社員一人につき複数のサークルに所属できる サークルは複数の社員をかかえることができる 1:1 社員情報 : 個人情報 社員一人につき一つの個人情報 ER図 IE記法  テーブル同士のリレーション(関係)を表現する 【○】・・・0 【|】・・・1 【鳥の足】・・・n リレーション関係で親と子の関係がある場合角を丸く表現する 中間テーブル n:nのリレーション設計する場合に中間に置くテーブル 中間テーブルには余計なカラムを持たせない リレーションの橋渡し役 SQLアンチパターン やってはいけない設計パターンを解説した書籍 【Amazon】https://www.amazon.co.jp/gp/product/4... ツール Cacoo MySQL Workbench 参考サイト 参考動画#1【リレーションER図】 DBの設計#2 正規化 データの繰り返しをなくして整理すること 手順は2つ 横方向の繰り返しをなくす 縦方向の繰り返しをなくす 例:青いカラムのデータが横方向に重複しているデータ このデータを縦方向に伸びるよう並べ替えて横方向の繰り返しをなくす 項目がグレーのカラムは縦に繰り返しが発生している この縦方向の繰り返しをなくすために、青いカラムのデータを別テーブルに切り出す 注文番号に紐付けて管理するようにテーブルを分けている 上記のように縦方向の繰り返しをなくすには1:nの関係のテーブルに切り出す その他の縦の繰り返しをそれぞれ切り出す 参考サイト 参考動画#2【正規化】 DBの設計#3 テーブル設計の手順 システム要件を把握 システムの要件と機能を明確にすること 要件例:AmazonのようなECサイト 機能例 - フロント画面 - 商品検索 - 商品詳細ページ - マイページ - 管理者画面 - ログイン - 商品管理 - 商品カテゴリー一覧 テーブルの概要設計(ラフスケッチ) テーブルの一覧と主要なカラムを書き出す 闇雲に出さず,機能一覧をみながらシナリオを思い浮かべながら洗い出す テーブル例 - 店舗テーブル - 店舗名 - メールアドレス - パスワード - 商品カテゴリーテーブル - カテゴリ名 - 商品テーブル - 商品名 - 価格 - 在庫 - ユーザーテーブル - 名前 - 住所 - メールアドレス - 購入履歴テーブル - 購入日 - 合計金額 テーブルの詳細設計(最終調整) 日本語(論理名)を英語名(物理名)に変換(命名規則参照) カラムに型を付ける varcharという型を使う場合、桁数が2の累乗数となる 例:2,4,8,16,32,64,128,256,512... ※256は255にする(256以上だとインデクスが貼れないから) 全てのテーブルに( id, created_at, updated_at )というカラムを3点セットで追加する id - **プライマリーキー** - int型 AutoIncrement - shop_idのような名前にしない **created_at / uptated_at** - datetime型 - _date*ではなく*_atと命名 ER図を書きながら正規化 1:nの関係のn側に外部キーを追加 例:shops |—————○Items この場合Itemsテーブルにshop_idカラムを追加 外部キー命名規則:相手テーブル名の(単数形_id) ER図を書きながら正規化していく 例:購入履歴テーブル|——○購入履歴明細テーブル|———|商 品テーブル(正規化で購入履歴テーブルから切り出す) n:nの関係のテーブルの間に中間テーブルを持たせる この場合外部キーは中間テーブルに持たせる インデクスや制約条件をつける インデクス(検索を早くするためのもの) - 検索のキーになるカラムに付ける - プライマリーキーや外部キーにはつけない→自動でつく **制約** - 全てのカラムに可能な限りつける 命名規則 半角アルファベット、半角数字、アンダーバー テーブル名は複数形、カラム名は単数形 text1,text2のような雑な名前にしない 予約後 If Null Limit Date など 予約後はテーブル名やカラム名には使わない このほかたくさんあるので、一覧を見ておくとよい 制約 NOT NULL制約 入力必須になる場合の制約 ユニークキー制約 値の重複を防ぐ制約 外部キー制約 リレーション先にレコードがあることを保証する制約 参考動画#3
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Springboot+Tymeleaf+Mybatis+MySQLでのCRUD処理実装(基本)

学習のためのCRUDの基本実装をしました。備忘録としてまとめました。 画面上にHelloWorld実装まではできるものとして書いてます。 MySQLにはあらかじめテーブルを用意しています。 開発環境 eclipse IDE Java 11 maven Tymeleaf Mybatis MySQL 設定ファイルの編集 application.propertiesにmySqlとの接続情報を記述 spring.datasource.url = jdbc:mysql://localhost/データベース名?serverTimezone=Asia/Tokyo spring.datasource.username = root spring.datasource.password = データベースのパスワード spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver spring.mvc.hiddenmethod.filter.enabled: true Pom.xmlへの依存関係の記述(プロジェクト生成時依存関係セットに足りないものを追加) <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>SymphogearTest</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SymphogearTest</name> <description>project for Spring Boot</description> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>ture</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>  実装コード コントローラークラス(画面操作と処理の実行をつなぐクラス) package io.spring.gungnir.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import io.spring.gungnir.dto.UserSearchRequest; import io.spring.gungnir.entity.User; import io.spring.gungnir.service.UserService; @Controller //コントローラークラスの宣言 public class UserController { @Autowired //サービスクラスのnewを自動でしてくれるアノテーション UserService userService; /* * URL(localhost:8080/user)でアクセスでトップ画面の表示 */ @GetMapping(value = "/user") //このURLにマッピングされておりGetするとメソッドが動く public String display() { return"top"; //top.htmlの画面を表示 } /* * 一件検索するためのフォームを表示 */ @GetMapping(value = "/user/search") //トップ画面でto search formを押下した時の画面遷移 public String displaySearch() { return"player_search"; //player_search.htmlの画面を表示 } /* * 一件検索の実行 */ @RequestMapping(value = "/user/id_search", method = RequestMethod.POST )    //searchボタンを押され/user/id_seachが呼ばれたとき動く。フォームに入力されたIDをpostで受け取る  public String search(@ModelAttribute UserSearchRequest userSearchRequest, Model model){   //@ModelAttributeがpostされたリクエストをuserSearchReqesutに格納する(同時にnewする)   User user = userService.search(userSearchRequest); //サービスクラスのメソッドにuserSerchReqestを引数として渡す model.addAttribute("user_info",user);     //サービスクラスメソッドの結果をuserに詰める。テンプレートで使うキーとして”user_info”を指定   return"player_search"; //player_search.htmlの画面を表示 } /* * 全件表示 */ @PostMapping(value = "/user/list") //player listボタンで/user/listが呼ばれたとき動く public String getUserList(Model model) { List <User> list = userService.searchAll(); //リスト型でサービスクラスのインスタンスメソッドを呼び出す(リスト取得の指示だし) model.addAttribute("users_info", list); //結果をlistに詰める。(users_infoがキー) return "list"; //list.htmlの画面を表示 }  /*新規プレイヤー登録画面を表示 * 画面遷移のみ */ @PostMapping(value = "/user/add") //to sing up formのボタンで/user/addが呼ばれたとき動く public String displayAdd() { return "add_player"; //add_playerの画面を表示 }  /*登録の実行 * 登録情報の表示 */ @RequestMapping(value = "user/add_comp", method = RequestMethod.POST )    //sign upが押され/user/add_compが呼ばれた時動く。フォームの内容をpostで受け取る  public String create(@ModelAttribute UserSearchRequest userAdd, Model model) { //postされたリクエストをusesrAddに格納+new   userService.create(userAdd);//追加処理の実行をサービスクラスに指示 User user = userService.createCheck(userAdd); //追加した情報をセレクトしとってくるよう指示 model.addAttribute("user_add", user); //セレクト結果をuserに詰める。(user_addをキー) return "add_comp"; //add_comp.htmlの画面を表示 }  /* * 編集画面への遷移 */ @PutMapping(value = "/user/conf/id={id}") //updateが押されたとき動く public String editSelect(@PathVariable("id")String id ,Model model){   //@PathVariableでURL内のidを引数に取得 User user = userService.editSelect(id); //取得したidを渡してセレクト指示 model.addAttribute("user_select",user); //セレクト結果をuserに詰める。(user_selectがキー) return "conf_player"; //conf_player.htmlを表示 }    /*   *編集の実行   */ @RequestMapping(value = "/user/edit/id={id}", method = RequestMethod.POST) //editボタンが押されたとき動く public String update(@ModelAttribute UserSearchRequest edit) { userService.update(edit); //postされたリクエストが格納されたeditを引数にupdateメソッド呼び出し return "edit"; //edit.htmlを表示 }  /*削除の実行 * */ @DeleteMapping(value = "user/delete/id={id}") //deleteボタンが押されたとき動く public String displayDelete(@ModelAttribute UserSearchRequest delete) { userService.deleteOne(delete); postされたリクエストが格納されたdeleteを引数にdeleteOneメソッド呼び出し return "delete"; //delete.htmlを表示 } } ``######サービスクラス(画面からのリクエストを実行するクラス) ```java package io.spring.gungnir.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import io.spring.gungnir.dto.UserSearchRequest; import io.spring.gungnir.entity.User; import io.spring.gungnir.repository.UserMapper; @Service //サービスクラスの宣言 public class UserService { @Autowired //userMapperを自動でnewしてくれる private UserMapper userMapper;   /*   * 一件検索表示 */ public User search(UserSearchRequest userSearchRequest) { return userMapper.search(userSearchRequest); //検索結果をリターンで受け取る }  /* * 全件表示 */ public List<User> searchAll(){ return userMapper.searchAll(); //全件のリストをリターンで受け取る } /* * 新規追加処理 */ public void create(UserSearchRequest userAdd) { userMapper.create(userAdd); }  /* * 追加情報を画面表示 */ public User createCheck(UserSearchRequest userAdd) { return userMapper.createCheck(userAdd); }    /* * 編集対象の検索 */ public User editSelect(String id) { return userMapper.editSelect(id); }  /* * 編集実行 */ public void update(UserSearchRequest edit) { userMapper.edit(edit); }  /* * レコード情報削除 */ public void deleteOne(UserSearchRequest delete) { userMapper.deleteOne(delete); } } マッパークラス(データベースを操作するクラス) package io.spring.gungnir.repository; import java.util.List; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import io.spring.gungnir.dto.UserSearchRequest; import io.spring.gungnir.entity.User; @Mapper //マッパークラスの宣言 public interface UserMapper { /* * プレイヤー情報検索 */ @Select("SELECT * FROM symphogear_players WHERE id = #{id}") User search(UserSearchRequest user); /* * 全件表示 */ @Select("SELECT * FROM symphogear_players") List<User> searchAll(); /* * 新プレイヤー情報登録 */ @Insert("INSERT INTO symphogear_players(id,name,symphogear_name)" + "VALUES (#{id},#{name},#{symphogear_name})") void create(UserSearchRequest userAdd); /* * 新追加情報表示 */ @Select("SELECT * FROM symphogear_players WHERE id = #{id}") User createCheck(UserSearchRequest userAdd); /* * 編集用の情報セレクト */ @Select("SELECT * FROM symphogear_players WHERE id = #{id}") User editSelect(String id); /* * 情報編集 */ @Update("UPDATE symphogear_players " + "SET name = #{name}, symphogear_name = #{symphogear_name} " + "WHERE id = #{id}") void edit(UserSearchRequest edit); /* * レコード削除 */ @Delete("DELETE FROM symphogear_players WHERE id = #{id}") void deleteOne(UserSearchRequest delete); } DTOクラス(画面からのリクエストデータを格納するクラス) package io.spring.gungnir.dto; import java.io.Serializable; import lombok.Data; @Data //セッター、ゲッターを記述しなくても使用できるようになる public class UserSearchRequest implements Serializable{ private String id; private String name; private String symphogear_name; } エンティティクラス(データベースから取得したデータを格納するクラス) package io.spring.gungnir.entity; import lombok.Data; @Data public class User { /* * ユーザー情報 entity */ private String id; private String name; private String symphogear_name; } 以上 長くなったので 画面周りのhtmlは次の記事→https://qiita.com/dende-h/items/9501d07c0e4088a0018c 感想・気づいたこと 一通りまとめたことで、処理の流れがつかめた。 途中で、実際には不要なコードがわかりより簡潔なコードにできた。 ここから入力チェックや例外処理など機能を増やしていく。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む