ぼくLog

子持ち、車持ち、マンション持ちの僕の日常を綴ります。車と、ラクをしたい一心で覚えたエクセルVBAを中心になりそうです。

データベースの処理にはクラスで対応しよう!|エクセルVBA

f:id:yt4u:20180210095024j:plain

エクセルのシートをデータベースのテーブルとして扱えるってめちゃくちゃ便利です。

yt4u.hatenablog.com

上記エントリーの最後にも触れているとおり、データベースに関係するコードをクラスで管理する方法を考えたいと思います。


クラスの構成要素を目次で示します。


内容は至ってシンプル。
前出エントリーに記載したコードのコピペでほとんどが完成します。



クラス化すればこんなにシンプルにデータが取れるようになる!

まずは、完成したクラスを使って、どんなコードを書けばデータを取ってこれるようになるのかを見たいと思います。

以下は、上記エントリーのデータベース関連処理をクラスに置き換えたものになっています。
前提条件等は上記エントリーをご参照ください。

また、クラスの名前は「blClsDataBase」としいています。

Public Sub getDataSample()

  Dim clsDB             As blClsDataBase
  Dim strSQL            As String
  Dim objRS             As Object
  
  
  Set clsDB = New blClsDataBase
  
  
  'クラスを生成(当エクセルに接続)
  
    With clsDB
    
      .DataBaseType = XLS
      .DataSource = ThisWorkbook.FullName
    
      .ConnectDB
    
    End With
  
  
  '抽出条件を作成
    
    strSQL = "SELECT"                                           '抽出フィールド(項目)を指定
      strSQL = strSQL & "  [名前]"
      strSQL = strSQL & ", [ふりがな]"
      strSQL = strSQL & ", [電話番号]"
      strSQL = strSQL & ", MONTH([誕生日]) AS [誕生月]"
    strSQL = strSQL & " FROM rngXDB_DataBase"                   'データテーブルを指定
    strSQL = strSQL & " WHERE 1 = 1"                            '抽出条件
      strSQL = strSQL & " AND [性別] = '男'"                    '性別=男
      strSQL = strSQL & " AND [年齢] >= 30"                     '年齢=30歳以上
      strSQL = strSQL & " AND [年齢] <  50"                     '50歳未満
      strSQL = strSQL & " AND [婚姻] = '未婚'"                  '婚姻=未婚
  

  '抽出実行
    
    If clsDB.GetRecordset(strSQL, objRS) = False Then
    
      Err.Description = clsDB.ErrNum
      
      GoTo ERR_PROC
    
    End If
    
  
  '抽出結果を出力
  
    With wsXLSDataBase
    
      With .Range("rngXDB_DataTop")
      
        
        '出力エリアにある既存データを消去
          
          .CurrentRegion.ClearContents
          
        
        'フィールド(項目)名を出力
          
          For lngF = 0 To objRS.Fields.Count - 1
          
            .Offset(, lngF).Value = objRS.Fields(lngF).Name
          
          Next lngF
          
        
        'データを出力
        
          .Offset(1).CopyFromRecordset objRS
          
    
      End With
    
    End With
  
  
  GoTo END_PROC
  
ERR_PROC:

  MsgBox clsDB.ErrDes
  
  
END_PROC:

  'DBクラスを閉じる
  
    Set clsDB = Nothing


End Sub


すごいシンプルですよね。
クラス化してしまえば、コードが非常に分かり易くなります。

データは取ってきてから色々と加工したりと複雑な処理が始まるワケで、だから尚のことデータを取ってくる部分はシンプルにしておきたいもの。

クラスを使うことで、それが実現できるわけです。
ただ、そのクラスを作るところでちょっとだけ手を動かす必要があります。

それを順に見ていきたいと思います。


データベースへの接続(コネクションの確立)

普段使うことになるデータベースはエクセルだけとは限りません。
アクセスを使うこともあるし、SQLServerを使うこともあります。

使用するデータベースが何であれ、ひとつのクラスで対応できちゃった方が断然ラク。
なので簡単に接続先を指定できるようにしたいと思います。



接続先を指定するためのプロパティを用意

クラスのプロパティを使います。
プロパティにどんな値を入れればいいのか選択肢が表示された方がコードを書きやすいと思います。
そこで列挙子を併用します。

Public Enum DBTYPE
  NONE
  XLS
  ACCESS
End Enum

Public DataBaseType               As DBTYPE


こうすることで、いざプロパティの値を入れるときに

f:id:yt4u:20180210101416j:plain

こういったカンジでオプションが出てきます。
選択肢の中から値を選ぶだけですから、コードを書くのがラクになりますよね。


この段階では、どこに接続するかを決めただけですから、実際に接続は行われていません。
接続を実行するためのメソッドを用意します。



エラーへの対応

ただその前に、データを取って来るときなどに何かしらエラーが発生することがあります。
例えば、SQL文が間違っていた、というそもそものエラーもありますよね。
それらエラーの内容を把握できるようにプロパティも用意しちゃいましょう。

Private mErrNum                   As Long
Private mErrDes                   As String


Public Property Get ErrNum() As Long

  ErrNum = mErrNum

End Property


Public Property Get ErrDes() As String

  ErrDes = mErrDes

End Property


クラス内でエラーが発生した場合は、そのエラー内容を「ErrNum」「ErrDes」で確認できるようになります。これらの具体的な使い方は、後のコード全文で確認してください。


接続の実行

準備が整ったところでいざ接続です。
データベースにエクセル、アクセスを使う際は、それらのバージョンによって接続文字列が異なるので注意。

Public Enum DBTYPE
  NONE
  XLS
  ACCESS
End Enum

Public DataBaseType               As DBTYPE

Public DataSource                 As String
Public ID                         As String
Public Password                   As String

Private mobjConnection            As Object

Private mErrNum                   As Long
Private mErrDes                   As String

Public Enum DBPROPERTY
  adStateClose = 0
  adOpenStatic = 3
End Enum


Public Function ConnectDB() As Boolean

  Dim strCNString       As String
  Dim strExtension      As String
  

  ConnectDB = False
  
  
  'データベースの種類に応じてConnectStringを用意
    
    Select Case Me.DataBaseType
    
      
      Case DBTYPE.NONE
        '指定忘れ
                
        Err.Description = "データベースタイプ選択エラー"
        
        GoTo ERR_PROC
        
        
      
      Case DBTYPE.XLS
        'エクセルの場合
        
        
        'データソース無指定はエラー
          
          If Me.DataSource = "" Then
          
            Err.Description = "DataSource指定エラー"
            
            GoTo ERR_PROC
          
          End If
      
      
        '拡張子に応じて接続文字列を分岐
          
          strExtension = LCase(Right(Me.DataSource, Len(Me.DataSource) - InStrRev(Me.DataSource, ".")))
              
                
          Select Case True
          
            Case LCase(strExtension) = "xls"
              '2003以前
            
              strCNString = "Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=Excel 8.0;"
            
            
            Case LCase(strExtension) Like "xls?"
              '2007以降
            
              strCNString = "Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;"
            
          
          End Select
        
        
          strCNString = strCNString & "Data Source=" & Me.DataSource & ";"
             
      
      
      Case DBTYPE.ACCESS
        'アクセスの場合
        
        
        'データソース無指定はエラー
          
          If Me.DataSource = "" Then
          
            Err.Description = "DataSource指定エラー"
            
            GoTo ERR_PROC
          
          End If
        
        
        '拡張子に応じて接続文字列を分岐
          
          strExtension = LCase(Right(Me.DataSource, Len(Me.DataSource) - InStrRev(Me.DataSource, ".")))
              
                
          Select Case True
          
            Case LCase(strExtension) = "mdb"
              '2003以前
            
              strCNString = "Provider=Microsoft.Jet.OLEDB.4.0;"
            
            
            Case LCase(strExtension) = "accdb"
              '2007以降
              
              strCNString = "Provider=Microsoft.ACE.OLEDB.12.0;"
            
          
          End Select
        
        
          strCNString = strCNString & "Data Source=" & Me.DataSource & ";"
          
          
          If Me.Password <> "" Then
          
            strCNString = strCNString & "JET OLEDB:DataBase Password=" & Me.Password & ";"
          
          End If
        
    
    End Select
    
    
  'コネクション確立
  
    Set mobjConnection = CreateObject("ADODB.Connection")
    
    mobjConnection.Open strCNString
    

    ConnectDB = True
    

  GoTo END_PROC
  
ERR_PROC:

  mErrNum = Err.Number
  mErrDes = Err.Description


END_PROC:


End Function


これで使いたいデータベースに接続することで出来るようになりました。



データベースに接続したら、あとは欲しいデータを取ってきたり、あるいはデータベースを更新したりすることになります。

それらもクラスの中で完結できるようにします。




SQLの実行

ここまで来れば、あとはSQLを実行するのみです。
SQLの実行結果、つまり滞りなく実行できたかどうかTrue/Falseを返してくれる関数を作ります。


レコードセットの取得(SELECT句)

Public Function GetRecordset( _
                        strSQL As String, _
                        objRecordset As Object _
                        )


  GetRecordset = False
  
  
  On Error GoTo ERR_PROC
  
  
  Set objRecordset = CreateObject("ADODB.Recordset")
  
  
  objRecordset.Open strSQL, mobjConnection, adOpenStatic
  
  
  GetRecordset = True
  
  
  GoTo END_PROC
  
ERR_PROC:

  mErrNum = Err.Number
  mErrDes = Err.Description
  

END_PROC:


End Function


SELECT句の場合は、引数にレコードセット・オブジェクトを持ちます。
SQL文を実行した結果、得られるレコードセットをそのオブジェクトに格納します。

SQL文の実行が問題なく行われれば、関数としてはTrueを返します。
一方、エラーが発生した場合はFalseを返しつつ、エラー内容を「ErrNum」「ErrDes」に格納します。
これにより、Falseだった時にそのエラー内容を確認することが可能になります。


レコードの追加・更新(INSERT, UPDATE句)

Public Function ExecuteDB( _
                        strSQL As String _
                        )


  ExecuteDB = False
  
  
  On Error GoTo ERR_PROC
  
  
  mobjConnection.Execute strSQL
  
  
  ExecuteDB = True
  
  
  GoTo END_PROC
  
ERR_PROC:

  mErrNum = Err.Number
  mErrDes = Err.Description
  

END_PROC:


End Function

引数にレコードセット・オブジェクトを持たないこと以外は、SELECT句の場合と同じです。



オブジェクトの破棄

レコードセットやコネクションといったオブジェクトの破棄もクラスで行えるようにします。


レコードセットの破棄

Public Sub CloseRS( _
                        objRS As Object _
                        )


  If Not objRS Is Nothing Then
  
    If objRS.State <> adStateClosed Then
    
      objRS.Close
    
    End If
  
  End If
  

End Sub


コネクションの破棄

Public Sub CloseCN()


  If Not mobjConnection Is Nothing Then
  
    If mobjConnection.State <> adStateClose Then
    
      mobjConnection.Close
    
    End If
  
  End If


End Sub


クラスの破棄と同時にコネクションも破棄されるようにする

上記のメソッドを使って個々のオブジェクトを破棄してもOKですが、クラス破棄と同時にコネクションを破棄するようにすれば手間が減らせます。

Private Sub Class_Terminate()

  Call CloseCN

End Sub



一度クラスを作れば使い回しが効く

これでデータベース処理用のクラスが出来上がりました。
作り上げるまでにかかる手間は、その後ラクになることで回収できます!

コード全文を掲載しますので、最初はコピペで使ってみてから、使いやすいようにアレンジしてみてください。

「blClsDataBase」

Option Explicit

Public Enum DBTYPE
  NONE
  XLS
  ACCESS
End Enum

Public DataBaseType               As DBTYPE

Public DataSource                 As String
Public SQLServerName              As String
Public SQLDataBaseName            As String
Public ID                         As String
Public Password                   As String

Private mobjConnection            As Object

Private mErrNum                   As Long
Private mErrDes                   As String

Public Enum DBPROPERTY
  adStateClose = 0
  adOpenStatic = 3
End Enum



Private Sub Class_Terminate()


  Call CloseCN
  

End Sub


'==========================================================
'エラー処理
'==========================================================
Public Property Get ErrNum() As Long

  ErrNum = mErrNum

End Property


Public Property Get ErrDes() As String

  ErrDes = mErrDes

End Property



'==========================================================
'DB接続
'==========================================================
Public Function ConnectDB() As Boolean

  Dim strCNString       As String
  Dim strExtension      As String
  

  ConnectDB = False
  
  
  'データベースの種類に応じてConnectStringを用意
    
    Select Case Me.DataBaseType
    
      
      Case DBTYPE.NONE
        '指定忘れ
                
        Err.Description = "データベースタイプ選択エラー"
        
        GoTo ERR_PROC
        
        
      
      Case DBTYPE.XLS
        'エクセルの場合
        
        
        'データソース無指定はエラー
          
          If Me.DataSource = "" Then
          
            Err.Description = "DataSource指定エラー"
            
            GoTo ERR_PROC
          
          End If
      
      
        '拡張子に応じて接続文字列を分岐
          
          strExtension = LCase(Right(Me.DataSource, Len(Me.DataSource) - InStrRev(Me.DataSource, ".")))
              
                
          Select Case True
          
            Case LCase(strExtension) = "xls"
              '2003以前
            
              strCNString = "Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=Excel 8.0;"
            
            
            Case LCase(strExtension) Like "xls?"
              '2007以降
            
              strCNString = "Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=Excel 12.0;"
            
          
          End Select
        
        
          strCNString = strCNString & "Data Source=" & Me.DataSource & ";"
             
      
      
      Case DBTYPE.ACCESS
        'アクセスの場合
        
        
        'データソース無指定はエラー
          
          If Me.DataSource = "" Then
          
            Err.Description = "DataSource指定エラー"
            
            GoTo ERR_PROC
          
          End If
        
        
        '拡張子に応じて接続文字列を分岐
          
          strExtension = LCase(Right(Me.DataSource, Len(Me.DataSource) - InStrRev(Me.DataSource, ".")))
              
                
          Select Case True
          
            Case LCase(strExtension) = "mdb"
              '2003以前
            
              strCNString = "Provider=Microsoft.Jet.OLEDB.4.0;"
            
            
            Case LCase(strExtension) = "accdb"
              '2007以降
              
              strCNString = "Provider=Microsoft.ACE.OLEDB.12.0;"
            
          
          End Select
        
        
          strCNString = strCNString & "Data Source=" & Me.DataSource & ";"
          
          
          If Me.Password <> "" Then
          
            strCNString = strCNString & "JET OLEDB:DataBase Password=" & Me.Password & ";"
          
          End If
        
      
    End Select
    
    
  'コネクション確立
  
    Set mobjConnection = CreateObject("ADODB.Connection")
    
    mobjConnection.Open strCNString
    

    ConnectDB = True
    

  GoTo END_PROC
  
ERR_PROC:

  mErrNum = Err.Number
  mErrDes = Err.Description
  

END_PROC:



End Function


'==========================================================
'SQL実行 - SELECT
'==========================================================
Public Function GetRecordset( _
                        strSQL As String, _
                        objRecordset As Object _
                        )


  GetRecordset = False
  
  
  On Error GoTo ERR_PROC
  
  
  Set objRecordset = CreateObject("ADODB.Recordset")
  
  
  objRecordset.Open strSQL, mobjConnection, adOpenStatic
  
  
  GetRecordset = True
  
  
  GoTo END_PROC
  
ERR_PROC:

  mErrNum = Err.Number
  mErrDes = Err.Description
  

END_PROC:


End Function



'==========================================================
'SQL実行 - SELECT
'==========================================================
Public Function ExecuteDB( _
                        strSQL As String _
                        )


  ExecuteDB = False
  
  
  On Error GoTo ERR_PROC
  
  
  mobjConnection.Execute strSQL
  
  
  ExecuteDB = True
  
  
  GoTo END_PROC
  
ERR_PROC:

  mErrNum = Err.Number
  mErrDes = Err.Description
  

END_PROC:


End Function


'==========================================================
'Recordsetを閉じる
'==========================================================
Public Sub CloseRS( _
                        adoRS As ADODB.Recordset _
                        )


  If Not adoRS Is Nothing Then
  
    If adoRS.State <> adStateClosed Then
    
      adoRS.Close
    
    End If
  
  End If
  

End Sub



'==========================================================
'Connectionを閉じる
'==========================================================
Public Sub CloseCN()


  If Not mobjConnection Is Nothing Then
  
    If mobjConnection.State <> adStateClose Then
    
      mobjConnection.Close
    
    End If
  
  End If


End Sub

Jige(じげ)で鮪の中落を食らう!

まずはこれ!

f:id:yt4u:20180120224407j:plain


鮪の中落!



こんな状態でお目にかかれるなんて、当の鮪でさえ想像していなかったことでしょう。


しかも、食べる道具は貝殻。


f:id:yt4u:20180120224456j:plain


貝だって身を守る殻を、身を削ぐ道具に使われるとは露程も思わなかったハズ。


貝殻を使って骨の間にある赤身をそぎ落としていきます。
そして結構、貝殻がちょうどいい。
鋭角から扇形まで多様な角度を持つ貝殻のエッジで、鮪の骨に着く赤身も簡単に取ることが可能です。


そりゃ間違いなく美味いですよね。
美味くないはずがない。

鮮度が抜群で、噛み締めると赤身の濃厚な味が口の中に広がります。
これを食べて美味しくないという人はまず居ないでしょう。

魚介類が苦手なぼくが言うんだから間違いないです。


この日は6人で食べたのですが、ものの数分でコレ。

f:id:yt4u:20180120224953j:plain



片面終わっても実は裏面もあります。


ひっくり返すときにお店からユッケ、なめろう、細巻きにすることも可能ですよ、という神の声。

このときは裏面のおよそ半分を貝殻で削ぎ食べて、残りの半分をなめろうに。



なめろうに仕立ててくれてる間に、お通し第二弾のイカ墨の味噌汁が登場。


f:id:yt4u:20180120231252j:plain


口を付けなくても烏賊の凝縮された香りが鼻腔を駆け抜けていきます。

決して臭みはなく、これを食べて美味しくないという人はまず居ないでしょう。

魚介類が苦手なぼくが言うんだから間違いないです。



あと(酔っぱらっていて)写真を撮るのを忘れてしまったのですが河豚のヒレ酒もあります。
しかもヒレが2つも入っていて贅沢な一品でオススメです。


この鮪の中落が食べられるお店はコチラ。

Jige 築地店


お店は綺麗で女性を連れて行っても怪訝な顔をされることはないでしょう。
家族連れでも大丈夫だと思います。
エラい人との接待なんかには不向きかとは思いますが、気心しれた人との接待なんかには丁度いいかもしれません。
インスタ映えはするかもしれませんが若者には不向きかとは思います。

ランチもお手頃で美味しいという口コミをよく見かけますので、ランチも行っていたいみたいなー。
その時は再度レポートします。

honor9は裸で持つのが最高!液晶フィルムとカバーは不要?

f:id:yt4u:20180108000615j:plain


まず先に言ってしまいます。


結論:honor9には液晶フィルムもカバーも必要なし



ひとまずhuaweiのhonor9のサイトでも見ながら検証しましょう。


consumer.huawei.com



理由1:ぴったり合う液晶フィルムがない


2.5D液晶の宿命です。

バシっと嵌るフィルムがありません。
これはhonor8でも同じ。如何ともしがたいものです。


でも、ご安心を。

液晶ガラスは Gorilla Glass(ゴリラガラス)。
世にあるスマホの多くが採用しているガラスです。

液晶に鋭利なものを当てるとか液晶面を地面に擦り付けるとかハードな使い方をすれば当然キズは付きます。

ただ、そんな使い方する人、いないですよね。

まともな使い方をしていれば、ちょっとやそっとではキズは付きません。


つまり、2.5D液晶にぴったり合うフィルムはないが、そもそも液晶ガラスは頑丈だから、液晶保護フィルムを貼る必要がない。


honor9は裸でOKです!



理由2:カバーを付けることで失うものが大きすぎる



第一に、背面の複層ガラスが織りなすオーロラのようなデザイン。カバーを付けるとこれが隠れてしまう。

これは非常にもったいないことです。
あたかも輝くダイヤの指を付けながらも、その上から手袋をするようなものですね。

やっぱり綺麗なものは見てもらってナンボです。



第二に、弛まぬ努力により薄く作られた筐体なのに、カバーを付けるとその努力を無にする等しい。

これはhonorに限った話ではないですが、せっかくスマホが薄くなってもカバーを付けたら台無しです。

確かに高価な端末であればカバーを付けたい衝動に駆られるのも仕方がありません。

ただ、honor9はどちらかというと廉価な部類(それでもぼくにとっては高級品ですが、、、)
honorを使う人は基本SIMも安いのを使ってコストカットしてるわけですから、落として傷付いて、あるいは壊れてもいいやくらいの寛大な気持ちで持つべき。


honor9は裸でOKです!



以上

ふるさと納税再考|牛肉はやっぱり定番ですが

f:id:yt4u:20171231174622j:plain


ふるさと納税の期間は、毎年1月1日から12月31日まで。


新たな年を迎えるとともに、ふるさと納税はリセットされることになります。 つまり、新年を新たな気持ちで迎える度に、ふるさと納税も新たな気持ちで臨むことが出来るわけです。


新たな気持ちで臨む前に、まずはこれまでのふるさと納税を振り返ってみたいと思います。



まずは牛肉です。




ふるさと納税で牛肉チョイスはど定番中のど定番。

ど定番であるが故に、牛肉は選択肢はめちゃくちゃ豊富。

あまりに沢山あるので選ぶだけでも下手すりゃ一日費やすことに(汗


ぼく、牛肉は有名どころからそこそこ有名どころまでいくつか試しました。
そんな中で、ぼくがチョイスした牛肉たちを振り返りたいと思います。



結論を先に言ってしまうと、味・触感はどこも一緒。


ブランド和牛のルーツは全て但馬牛だと言われています。
なので、そこそこ有名どころ以上の牛肉は基本、同系統の肉質だと言って差し支えなさそうです。

あとは、牛が育った環境などに左右されるわけですが、正直、シロートにはそんなの分かりっこありません。
だから運に任せるしかないっす(開き直り


ただ、同価格帯で比較した場合、超有名どころはブランドプレミアムがある分、そこそこ有名どころと比べるとやや格下のお肉が来る。それがぼくの結論です。
なので、選ぶべきはブランドプレミアムが大きくない、そこそこ有名どころということになります。
そういった処の方がコスパ・満足度が圧倒的に高いです。
(あくまでもぼくの個人的見解です)



ということで、個人的見解に基づくぼくのオススメを挙げていきたいと思います。



鹿児島県鹿屋町


まず一番満足度が高かったのが鹿屋町の鹿児島県産黒毛和牛A-5等級 ロースすき焼き用700g。

www.furusato-tax.jp


どうしても牛肉と言えば、「舌触りが良くて口に入れると直ぐに溶けていく」。そんな感覚に憧れを抱いてしまいます。
ただ、そういった牛肉の多くは脂身が多いことと表裏一体。

つまり、口の中で溶ける=脂が溶ける。ただそれだけになりがちです。
そういった牛肉は少し食べただけでも脂が胃袋に「ずんっ」と来て、あっという間にお腹一杯&胃もたれ確定です。


そんな中、上記の牛肉は、謳い文句の「濃厚で甘みが強く、脂・旨み・後味のバランスがパーフェクトなA5等級のロースをなんと700g。」に偽りなし。 脂身の「ずんっ」と来る感覚は最小限に留めつつ、程よい口溶けを残しながら肉の旨味を堪能させてくれます。


つまり、



めちゃくちゃオススメです。

honor9いいね

ゲットしましたhonor9。


先日、取り急ぎ簡単にご報告しておりました。

yt4u.hatenablog.com


実際、手元に届いたので、簡単に所感を述べたいと思います。

f:id:yt4u:20171111214603j:plain


選んだ色はスペースグレー。

サファイアブルーも惹かれましたが、実際使うヨメの希望です。無難がいいと。

f:id:yt4u:20171111214704j:plain

しっかし、背面ガラスはホントきれい。 ケースに入れずに裸で持つ場合、他の人にはこの背面ガラスが目に入るわけですが、視線を独り占めすること間違いなしっ!(言い過ぎ

起動します。

f:id:yt4u:20171111214800j:plain

honor8と同じ立ち上がり。


そして、このあと各種設定をしていくわけですが、結局、使い勝手はどうなのよ?ですよね。

それに関しては、ぼくが使っているhonor8との比較感になりますが、











何も変わらないっ!!






そう、何も変わらないんです。


ホームボタンやナビゲーションボタンが液晶下に移動しているので、honor8と何も変わらないと言い切ってしまうのは語弊がありまますが、使用感についてhonor8との明確な差異は認められませんでした(ぼく調べ)



honor8 → 9 で大きな進化が無かったわけですが、逆に言うとhonor8が既に完成されたものだったんですね。だから、進化のしようがなかった。


(家計の都合上、honor8からの買い替えを許されなかったぼくの負け惜しみが入ってます)



honor9を悪く言うつもりではありません。逆に有り難いと思いました。

世の中のスマホは、必要以上の機能を付けては価格が上がっていくという流れの中にいます。

その中において、必要にして十分な機能とスペックを兼ね備え、かつ非常にリーズナブルな価格でそれを提供してくれる。

まさにhonorシリーズのお家芸と言えますね。



honor8を独占的に扱っていた楽天モバイルにおけるhonor9の価格は上がっていたので、honorらしさを失ってしまったのかと危惧しておりましたが、OCNがしっかりとhonorらしい価格帯で提供してくれていたのが救いです。honorのIdentittyは崩れていないと。



こんなモンを世に放ってくるhuawei。ホントにすごいです。