5

私は最近追加された「BLA "BLAHBLAH" Ltd.」という名前の国際的な会社を持っています (二重引用符は名前の一部です。)

ユーザーがこの会社を検索しようとするたびに、「何とか」またはそれに関連する何かを入力すると、検索は SQL サーバーの構文エラーで失敗します。

検索が失敗しないようにするにはどうすればこれをエスケープできますか?

サンプル SQL:

SELECT c.companyID, c.companyName, c.dateAdded, count(cm.maxID) as NumDirect
    FROM RussoundGeneral.dbo.Company c  
         LEFT JOIN RussoundGeneral.dbo.CompanyMax cm
                ON (cm.companyId = c.companyId and cm.maxID is not null)  
    WHERE CONTAINS ( companyName,  '"BLAH*' )
    GROUP BY c.companyID, c.companyName, c.dateAdded  
    ORDER BY c.companyName ASC
4

8 に答える 8

8

残念ながら、二重引用符は FTI 内で特別な意味を持っているため、パラメーター化しても、FTI エンジンはそれを句の区切りとして扱います。FTI 検索に二重引用符を含める簡単な方法があるかどうかはわかりません。ブラケットも特殊文字ですが、クエリ用語として扱うために引用符で囲むことができますが、AFAIK の二重引用符ではありません。

アップデート

少し検索すると、引用符を "" に二重化すると修正される可能性があることが示唆されます-試してみる価値があります。これは TSQL 実装の詳細であるため、個人的には DB 内でこれを行います。

同様に、FTI に渡す前に ' を 2 倍にして '' にする必要があります (TSQL エスケープとは完全に別です)。

于 2008-12-22T20:22:58.697 に答える
7

パラメータ化されたクエリを使用すれば、引用に関する問題はすべて解消されます。

編集: CONTAINS に複数の単語を入力させない場合は、引用符を削除してパラメーターをサニタイズします。引用符を削除して入力をサニタイズすることは、複数単語検索に関係なく、とにかく機能する可能性があります。

于 2008-12-22T20:14:19.010 に答える
3

SQLを動的に構築していると強く思います-たとえば

// Bad code, do not use!
string sql = "SELECT * FROM Foo WHERE X LIKE '" + input + "%'";

これは、多くの理由から非常に悪い考えです。最も顕著なのはSQL インジェクション攻撃です。代わりに、パラメーターを個別に指定するパラメーター化された SQL ステートメントを使用してください。

これを適切に行う方法の例については、 sql-injection タグを使用した質問に対するさまざまな回答を参照してください。

于 2008-12-22T20:12:50.590 に答える
1

これは少し理論的な答えですが、役立つかもしれません。短いバージョンは「クエリでパラメーターを使用する」ですが、詳細を理解するのに役立ちます。

標準 SQL では、文字列は単一引用符で囲まれ、埋め込まれた単一引用符は 2 つの単一引用符で表されます。

SELECT * FROM SomeWhere
    WHERE SomeThing = 'He said, "Don''t do it!"';

一部の SQL 方言では、代わりに文字列を二重引用符で閉じることができます。二重引用符の単一のインスタンスを埋め込むには、二重引用符を二重にする必要があります。

SELECT * FROM SomeWhere
    WHERE SomeThing = "He said, ""Don't do it!""';

会社名に外側の二重引用符と中央の二重引用符が含まれているのか、それとも中央の二重引用符だけが含まれているのかは、質問からは明らかではありません。ただし、原則としてルールは同じです。3 つの二重引用符がすべて必要であり、SQL で単一引用符を使用すると仮定すると、このコンテキストでははるかに簡単になります。

SELECT c.companyID, c.companyName, c.dateAdded, count(cm.maxID) as NumDirect
    FROM RussoundGeneral.dbo.Company c  
         LEFT JOIN RussoundGeneral.dbo.CompanyMax cm
                ON (cm.companyId = c.companyId and cm.maxID is not null)  
    WHERE CONTAINS ( companyName,  '"BLAH "BLAHBLAH" Ltd.' )
    GROUP BY c.companyID, c.companyName, c.dateAdded  
    ORDER BY c.companyName ASC;

二重引用符の使用:

SELECT c.companyID, c.companyName, c.dateAdded, count(cm.maxID) as NumDirect
    FROM RussoundGeneral.dbo.Company c  
         LEFT JOIN RussoundGeneral.dbo.CompanyMax cm
                ON (cm.companyId = c.companyId and cm.maxID is not null)  
    WHERE CONTAINS ( companyName,  """BLAH ""BLAHBLAH"" Ltd." )
    GROUP BY c.companyID, c.companyName, c.dateAdded  
    ORDER BY c.companyName ASC;

プログラミング言語で文字列を作成している場合は、プログラミング言語で文字列を評価するものを超えてこれらの引用符を取得することについて心配する必要があります。たとえば、C で文字列リテラルを作成する場合は、二重引用符をバックスラッシュでエスケープする必要があります。

static const char sql_stmt[] =
"SELECT c.companyID, c.companyName, c.dateAdded,\n"
"       COUNT(cm.maxID) AS NumDirect\n"
"    FROM RussoundGeneral.dbo.Company c\n"
"         LEFT JOIN RussoundGeneral.dbo.CompanyMax cm\n"
"                ON (cm.companyId = c.companyId AND cm.maxID IS NOT NULL)\n"  
"    WHERE CONTAINS(companyName,  \"\"\"BLAH \"\"BLAHBLAH\"\" Ltd.\")\n"
"    GROUP BY c.companyID, c.companyName, c.dateAdded\n"
"    ORDER BY c.companyName ASC";

一方、ユーザーから会社名などのデータを読み取る場合は、読み取った内容が正しく引用されていることを確認する必要があります。

「パラメーターを使用する」と言った人は正しいです。これははるかに簡単で信頼性が高く、SQL インジェクション攻撃に対して脆弱ではありません (まだ見ていない場合は、XKCDを参照してください)。しかし、基礎を理解していれば、システムの実際の要件に適応できます。

最後の注意: 標準 SQL では、二重引用符で「区切り識別子」を囲みます。つまり、二重引用符で囲まれた名前は、文字列リテラルとしてではなく、データベース内の何かの名前として扱われる必要があります。MS SQL Server では、[角括弧] は同じ目的を果たします。角かっこの間にあるのは、列の名前またはデータベース内の名前です。多くのシステムはそれよりも柔軟です。標準からどのように逸脱するかという点で、すべてのシステムが同じというわけではありません。

于 2008-12-22T21:34:50.923 に答える
0

文字をASCIIコードに置き換えてみましたか?

于 2008-12-22T20:33:21.907 に答える
0

エスケープキーワードを使用してみてください:

SELECT c.companyID, c.companyName, c.dateAdded, count(cm.maxID) as NumDirect 
FROM RussoundGeneral.dbo.Company c          
LEFT JOIN RussoundGeneral.dbo.CompanyMax cm 
ON (cm.companyId = c.companyId and cm.maxID is not null )  
WHERE CONTAINS ( companyName,  '\"BLAH*' ) escape '\'
group by c.companyID, c.companyName, c.dateAdded  ORDER BY c.companyName ASC
于 2008-12-22T20:38:06.280 に答える
0

次のようなものでなければなりません

string sqlCommand = "SELECT c.companyID, c.companyName, c.dateAdded, count(cm.maxID) as NumDirect FROM RussoundGeneral.dbo.Company c LEFT JOIN RussoundGeneral.dbo.CompanyMax cm ON (cm.companyId = c.companyId and cm.maxID is not null ) WHERE CONTAINS ( companyName,  '@strVal' ) group by c.companyID, c.companyName, c.dateAdded ORDER BY c.companyName ASC"
SqlCommand command = new SqlCommand(strSQLCommand, conn); 
SqlCommand.Parameters.AddWithValue("@strval", SearchTextBox.Text); 
于 2008-12-22T21:15:38.980 に答える
0

最後に、データベースからデータを抽出し、画面に表示するか、レポートに印刷する必要があります。二重引用符や余分な文字を操作すると、非常に混乱する可能性があります。

INSERTS または UPDATES の前に文字列を HTML に変換することにより、引用符の管理に関連するすべての混乱を回避できます。SELECT 時に、HTML から元に戻すのは簡単です。レポート作成時には (レポート ツール (Crystal Reports など) では HTML 形式のオプションが提案されているため)、データを正しい方法で表示するために何もする必要はありません。

ちなみに、この社名を発明した男を吊るすことを忘れないでください。

于 2008-12-23T07:22:40.643 に答える