スキップしてメイン コンテンツに移動

[エッセンシャル版] マイケル・ポーターの競争戦略 (ジョアン・マグレッタ著)

マイケル・ポーターは「Competitive Advantage (競争優位)」「Five Forces」で有名な経営戦略研究の第一人者です。私も以前から興味があり名著「競争の戦略」を読んでみようと思っていたのですが、書店に行って、あまりの本の分量の多さ (+価格の高さ)に購入を諦めていました。



もっと手軽に読めそうな本はということで、以下のポーターの論文集のような「競争戦略論I」「競争戦略論II」本は購入したのですが、いまいち(私のレベルの)実務では実感に乏しく、あまり深く読んで理解することができませんでした。



そんな中でようやく見つけたのが本書「[エッセンシャル版] マイケル・ポーターの競争戦略」になります。結論から言いますと、本書が私にとっては最も理解しやすかったです。


本書は、ポーターの経営・競争戦略の理論の本質を、ポーターと長年仕事を共にしてきたジョアン・マグレッタが要約し解説する形で書かれています。

原著は「Understanding Michael Porter: The Essential Guide to Competition and Strategy」になります。


今回の邦訳の題名は意訳感はないので改めて訳すほどではないですが、私の拙い英語で直訳すると「マイケル・ポーターを理解する: 競争と戦略の必須ガイド」になるでしょうか。余談ですが「Essential Guide」は「エッセンシャルガイド」のカタカナで訳
してしまってもよいかも知れません、日本語でぴったり来る言葉が見つかりませんでした。そもそも「Guide」は「ガイド」と訳すしかないような気がします。

さて本の内容です。正直あまり期待していなかったのですが、ポーターの競争戦略の要点がしっかりまとめられており、「なるほど」「面白い」と感じながら読み進めることができました。また、ポーターの理論へのよくある批判に対する反論や解説も記されており、ポーターの理論を俯瞰的に理解することができました。

私が、本書でまず気づかされたのは「競争に勝つ」という言葉に対する意味のとり方です。我々は「競争に勝つ」というとどうしても「最高を目指す」「一番になる」「相手を打ち負かす」というような行動をとりがちですが、ポーターは「最高を目指すべきではない」と言明しています。同じものさしで最高を目指して、すべての企業が競争すれば、最終的に相手を打ち負かすまで続く消耗戦になる上、顧客に提供できる価値の種類も少なくなってしまい顧客にとっても不幸だ、とポーターは述べています。ここでふと頭に思い浮かんだ例が、日本のECサイトの競争です。例えばAmazon、楽天、Yahooショッピング、ヨドバシカメラを比較する際、我々はどうしても流通総額(規模)で単純比較しがちですが、実は各社とも独自性があり、異なる顧客のニーズを満たしていると思います。

「戦略とは何か?」で上げられていた要素は、当たり前といえば当たり前ですが、本質を突いていると感じました。
  • 価値創造
  • トレードオフ
  • 適合性
  • 継続性
この本を読んでから、私もチームを率いていく上で、「何の価値を成し遂げるために(価値創造)」「何をとって何を捨てて(トレードオフ)」「現在の現場の体制に合うように(適合する)」「長くつづける(継続性)」を絶えず意識するようになりました。
例えば、システムのチームとして「少ない人員で高速にシステムを開発するために(価値創造)」「システムの文書化や多少のバグは許容されるので手動テストは捨てて、徹底的にコードを書く、自動化に注力し(トレードオフ・適合性)」「チームのメンバーが入れ替わっても3年間続ける(継続性)」というように、4つの要素を考慮しながら方針(戦略)を決めるようになりました。環境が違えば、時間は余分にかかっても「コードを書く」よりも「システムの文書化」の方を優先せざるを得ない場合もあると思います。これがポーターの言う「適合性」なのだと勝手に(汗)解釈しています。
本書で挙げれている4つの要素は、経営に限らず一般的に何かを成し遂げる上で、役に立つものだと思いました。

最後に、本書の終章で挙げられていた「10の実践的な意味」はどれも落とせない指針ですが、その中で特に私が特に印象に残ったものを引用します。
1. 最高を目指す競争は、一見正しいように思えるが、実は自己破壊的な競争方法である。
2. 利益を生まない規模拡大や成長には、何の意味もない。競争の目的は市場シェアではなく、利益にある。
...
5. あらゆる顧客を満足させようと思わないこと。一部の顧客を意図的に不満にさせるのが、優れた戦略の特徴である。
6. 戦略は組織がやらないことをはっきり打ち出して、初めて意味をもつ。トレードオフは、競争優位を実現し持続させる、戦略のかすがいだ。
...
8. 優れた戦略は、1つではなく多数の選択に立脚しており、さまざまな選択間の結びつきのうえに成り立っている。ひとつのコアコンピタンスが持続可能な競争優位を生み出すことはまずない。
(251~252ページ)
「エッセンシャル版」と銘打たれているとおり、コンパクトにまとまっていて読みやすい本だと思います。仕事の実務の指針を考える上でも非常に役立ちました。本書は☆5つでお勧めです。

コメント

このブログの人気の投稿

Eclipseでコードカバレッジのハイライトを削除する方法

Eclipseには便利なコードカバレッジ表示機能が搭載されていますが、コード内に緑、赤、黄の色付けがされて煩く感じるときもあると思います。 1度カバレッジの色付けが出てしまった後に消す方法の紹介です(方法は簡単)。 下記のキャプチャの青いマーカーで示した「Remove All Sessions」のボタンを押せばすべて消えます。

「特定の文字から始まらない文字列」にマッチする正規表現

「特定の文字から始まらない文字列」 にマッチする正規表現の例です。  以下の例では、Aから始まらない文字列にマッチする正規表現を示しています。 ^(?!A).*$ 私も正規表現の組み方で四苦八苦することがあります。以下の書籍は実践的に様々な正規表現のパターンを例示してくれているので、重宝しています。

ダイソーで買った200円のドライバーセットでHDDを分解

HDDの処分 最近は個人情報の問題もあって、HDDを処分する前にちゃんとデータの消去を気にすることも多くなってきました。消去方法としては大きく分けて下記の3つがあります。 データ消去ソフトでフォーマット HDD内部のプラッタを物理破壊 データ消去を行ってくれる専門の業者や家電量販店(Sofmapやビックカメラで実施していると思います。費用発生。)に持ち込み。 データ消去ソフトでのフォーマットは簡単ですが、欠点として「フォーマットに時間がかかる」「セクタ破損などで中途半端に壊れたディスクのフォーマットができない」などがあります。 またHDD内部のプラッタの物理破壊については、HDDを分解するために、通常のプラスやマイナスドライバーではなく、星形ネジに対応したトルクスドライバーが必要とのこともあって、少し面倒です。 筆者は今回、今後もHDDの廃棄をするだろうなあと思い、思い切って自分で分解して廃棄することにチャレンジしてみました。(家電量販店に持って行くよりも安くできないかというどケチ丸出しですw) HDDの星形ネジ こんなやつです。ちなみに写真はSeagateのST2000DL003というHDDで撮影しました。 トルクスドライバー というわけで、分解のために Amazonでトルクスドライバー を探しました。 調べると T8のもだと使えそう とのことで、いろいろと物色。 セットのものとか T8一本で立派なやつとか 色々あったのですが、HDD壊すだけで800円かぁ(←どケチ)、と思って購入を躊躇。 ネット上で調べると100円ショップのダイソーでも、トルクスドライバーを販売しているとの情報をキャッチ!近所のダイソーに行って、探したところ星形のヘッド交換に対応した精密ドライバーセットがありました。 プラスが10種類、マイナスが8種類、六角が6種類、星形が6種類(今回ほしかったもの)のセットで、何とお値段税抜き200円!、税抜き200円!と安かったので、ダメもとで購入しました。 結論から言うと 買って大正解 でした。 ダイソーの精密ドライバーセット こんな商品です! 星形対応のヘッドを装着するとこんな感じ。ドライバーのグリップもゴムで滑らない様になっていて使いやす

SQLで特定の文字を組み合わせたランダムな文字列を生成

簡易的な方法として「指定した文字列からランダムに1文字選ぶ」を必要な文字の長さ分concat関数でつなげれば実現できます。 1文字ずつ文字を選ぶので、あまり性能もよくない上、セキュリティ的な観点からのランダム性も担保されていないので、あくまで開発中に必要になった時に使う程度が無難だと思います。 下記に英数字大文字小文字を含んだランダムな3文字の文字列を生成するクエリを示します。 # RAND関数で指定した文字列からランダムに1文字選択。 # 下記の例の62の部分はa~z、A~Z、1~9の文字数の合計値を入れた結果 SELECT CONCAT( SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', FLOOR(RAND() * 62 + 1), 1), SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', FLOOR(RAND() * 62 + 1), 1), SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', FLOOR(RAND() * 62 + 1), 1) ) AS random_string;

PHPの配列(array)のメモリ使用量の考察

はじめに 最近PHP上に大量のデータをメモリ上に展開していたのですが、配列(array)の形式(連想配列 or 単純な配列)や配列の要素のデータ構造(数字、配列、文字列など)で大きくメモリ使用量に差が出てくることに気づき、簡単なプログラムを組んで調べてみました。 あくまで筆者の環境での結果なので、細かい数値は参考程度に見てください。 測定環境と方法 OS: Windows 10 PHP 7.4.5 (php-7.4.5-nts-Win32-vc15-x64) 配列に要素を追加するプログラムを書いて、PHPのmemory_get_usage(true)関数を使って実メモリ使用量を計測しました。 計測結果 No. 方式 1MB当たり作成できる 要素数 プログラム 補足 1 キーも値も整数の配列 (整数IDを想定) 28571 // 2,000,000 / 70MB $row = []; for($i = 0; $i < 2000000; $i++) { $row[] = $i; } No.2~6でテストしたプログラム中の要素数は200,000。これだけ一桁多い! 2 キーが文字列、値が整数の連想配列 8333 // 200,000 / 24MB $row = []; for($i = 0; $i < 200000; $i++) { $row[$i.'_key_string'] = $i; } キーの文字列が長い方がメモリ使用量多くなる。 3 キーが整数、値が連想配列の配列 DBから取得してきたデータを想定 2325 // 200,000 / 86MB $row = []; for($i = 0; $i < 200000; $i++) { row[] = ['id' => $i]; } 4 キーが整数、値が連想配列の配列(配列に複数の値を保持) DBから取得してきたデータを想定 2127 // 200,000 /

ADODB.streamオブジェクトを使って文字列とByte配列を相互変換(Excel VBA)

ADODB.streamオブジェクトを使って文字列をByte配列に変換するコードのサンプルです。 ExcelVBAでADODB.streamを使う際には、 1. ExcelのMicrosoft Visual Basic エディタのメニューバーから「ツール->参照設定」とたどる。 2. 表示されたダイアログからMicrosoft ActiveX Data Objectsにチェックを入れる。 という手順が必要です。 文字列からByte配列へ Private Function ADOS_EncodeStringToByte(ByVal cset As String, ByRef strUni As String) As Byte() On Error GoTo e Dim objStm As ADODB.stream: Set objStm = New ADODB.stream objStm.Mode = adModeReadWrite objStm.Open objStm.Type = adTypeText objStm.Charset = cset objStm.WriteText strUni objStm.Position = 0 objStm.Type = adTypeBinary Select Case UCase(cset) Case "UNICODE", "UTF-16" objStm.Position = 2 Case "UTF-8" objStm.Position = 3 End Select ADOS_EncodeStringToByte = objStm.Read() objStm.Close Set objStm = Nothing Exit Function e: Debug.Print "Error occurred while encoding characters" & Err.Description If objStm Is No

MySQLのSQL_CALC_FOUND_ROWS使用上の注意

MySQLのSQL_CALC_FOUND_ROWSを使用する際の無駄なメモリ消費に注意 ページング機能の実装などのために、ヒットした全件数を取得する必要のある場合があるかと思います。 その場合、下記のようにSQL_CALC_FOUND_ROWSを使うと、検索結果に加えて、そのクエリの全ヒット件数が取得できます。 SELECT SQL_CALC_FOUND_ROWS table.column1, table.column2 ...(途中省略)... FROM table WHERE (条件) しかし、SQL_CALC_FOUND_ROWSを使うと、「絞り込んだ結果にヒットする全べての行の結果のセットを作成する」という大きな欠点があります。 これは、LIMIT, OFFSETを指定していても実行されてしまうので、 SELECTで指定するカラム数やデータ量が多い SELECTの結果返ってくる行数が多い 場合、無駄に領域(≒メモリ)を消費してしまうので注意が必要です。 例えば、100万行検索対象としてヒットするクエリの場合、仮にLIMIT 20と指定して最初の20行を取得するようにクエリを書いても、その100万行分の結果セットが作成されてしまうということになります。 対応策 根本的な対応策としては、SQL_CALC_FOUND_ROWSを使わない(結果行数の取得はCOUNTを用いた別クエリで取得する、結果行数をあらかじめサマリーテーブルに保持しておく)ことですが、 SQL_CALC_FOUND_ROWSをどうしても使う必要がある場合は、 SELECT SQL_CALC_FOUND_ROWS primary_id のように最低限のカラムを指定して結果行セットを取得 (LIMIT OFFSET指定前提) 取得したprimary_idを使って必要なデータを取得 して、SQL_CALC_FOUND_ROWSで使用する領域をできるだけ減らすことで、対応するしかないと思います。 世の中ではLIMIT, OFFSETは使わない方がよいとよく書かれていますが、 SQL_CALC_FOUND_ROWSは、書いてしまえばどんなときも検索にヒットする全結果行セットを作成するので、同じくらい使用する際には注意が必要です。

Visual Studio 2010 SP1のアンインストール

Visual Studio 2013に乗り換えるためにVisual Studio 2010をアンインストールしようとしたところで問題発生。。。 先にVisual Studio 2010本体をアンインストールした後、Visual Studio 2010 SP1をアンインストールできなくて困っていました。 Google先生で調べたところ、以下の情報が見つかり、書かれていた通り実施したところ無事Visual Studio 2010 SP1のアンインストールに成功しました。 How to uninstall/remove Visual Studio SP1 アンインストール手順は以下の通りです。 http://www.microsoft.com/en-gb/download/details.aspx?id=23691 からMicrosoft Visual Studio 2010 Service Pack 1 (Installer)をダウンロード VS10sp1-KB983509.exeというファイル名でダウンロードされる(はず)。 コマンドプロンプトから以下のコマンドを実行 (以下の例は、c:\tempにVS10sp1-KB983509.exeがある場合) c:\temp\VS10sp1-KB983509.exe /uninstall /force ダイアログが立ち上がるので、アンインストールを選択して次へ進めばOK!

WinHttp.WinHttpRequestを使ってHttp PostでByte配列やString配列を送信するプログラム(Excel VBA)

WinHttp.WinHttpRequestオブジェクトを使って使ってHttp PostでByte配列のデータを送信するExcel VBAプログラムです。 WinHttp.WinHttpRequestを使う際には、 1. ExcelのMicrosoft Visual Basic エディタのメニューバーから「ツール->参照設定」とたどる。 2. 表示されたダイアログからMicrosoft WinHTTP Serviceにチェックを入れる。 という手順が必要です。 Byte配列をPOST Private Const BOUNDARY As String = "SOMETHING" Private Function httpPostServletByte(url As String, data() As Byte) As Byte() On Error GoTo e Dim WinHttpReq As WinHttp.WinHttpRequest If (WinHttpReq Is Nothing) Then Set WinHttpReq = New WinHttpRequest End If WinHttpReq.Open "POST", url, False WinHttpReq.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded; boundary=" & BOUNDARY WinHttpReq.send data Dim response() As Byte: response = WinHttpReq.ResponseBody httpPostServletByte = response Set WinHttpReq = Nothing Exit Function e: Set WinHttpReq = Nothing Debug.Print "httpPost Error:" & Err.

MySQL: SELECTの結果をUNIONして ORDER BYする際の最適化方法

SELECTの結果をUNIONして ORDER BY する際には下記の点に注意する必要があります。 無駄なメモリ消費 ソートにINDEXが利かない (≒CPU負荷増大) 対応策 可能であればPush-down Condition (各サブクエリ内でORDER BY, LIMIT, OFFSETを適用してからUNION, ORDER BYを実行する)を利用することで、 パフォーマンスを改善できる場合があります。 下記に例を示します。 もともとのクエリ SELECT tmp.* FROM ( SELECT tableA.column1, tableA.column2 FROM tableA WHERE (条件) UNION ALL SELECT tableB.column1, tableB.column2 FROM tableB WHERE (条件) ) AS tmp ORDER BY tmp.column1, tmp.column2 LIMIT 100, 20 Push-down Conditionを用いて書き直したクエリ SELECT tmp.* FROM ( SELECT tableA.column1, tableA.column2 FROM tableA WHERE (条件) ORDER BY tableA.column1, tableA.column2 LIMIT 30 # <- 10 (offset) + 20 (limit) UNION ALL SELECT tableB.column1, tableB.column2 FROM tableB WHERE (条件) ORDER BY tableB.column1, tableB.column2 LIMIT 30 # <- 10 (offset) + 20 (limit) ) AS tmp ORDER BY tmp.column1, tmp.column2 LIMIT 10, 20 ただしこのPush-down Conditionの手法も下記の場合は、効果が半減しますので注意が必要です。 OFFSETの値が大きい場合は、結局全結果セットUNIONと変わらない サブクエリ内のソートで、INDEXが効かない場合