2010-07-07 2 views
3

Est-il possible avec LINQ to SQL de rechercher dans toute la base de données (évidemment uniquement les parties mappées dans le fichier .dbml) pour une correspondance de chaîne? J'essaye d'écrire une fonction qui prendra une chaîne de "terme de recherche" et rechercherai toutes les entités mappées et retournerai une liste (d'objet) qui peut contenir un mélange d'entités si j'ai une table "Foo" et une table " Barre "et recherche de" wibble ", s'il y a une ligne dans" Foo "et une dans" Bar "qui contient" wibble "je voudrais retourner une liste (Of Object) qui contient un objet" Foo "et un" " Bar "objet. Est-ce possible?Utilisation de LINQ to SQL pour rechercher toute la base de données

+0

C# ou VB.Net? S'il vous plaît faites attention à l'étiquetage. –

+1

Voir http://msdn.microsoft.com/en-us/vbasic/bb737939.aspx#strcont ou http://msdn.microsoft.com/en-us/vbasic/bb688085.aspx pour de nombreux autres exemples. –

+1

@Serkan, pourquoi est-ce important? Ils devraient tous les deux avoir les mêmes méthodes et je ne veux pas limiter qui répondra en fonction de la langue, cela ne me dérange pas vraiment - je peux les traduire les uns aux autres. – Ben

Répondre

7

LINQ to SQL, ORM en général, même SQL est une mauvaise correspondance pour une telle requête. Vous décrivez une recherche en texte intégral, vous devez donc utiliser la fonctionnalité de recherche en texte intégral de SQL Server. Full Text Search est disponible dans toutes les versions et éditions depuis 2000, y compris SQL Server Express. Vous devez créer un catalogue FTS et écrire des requêtes qui utilisent les fonctions CONTAINS, FREETEXT dans vos requêtes.

Pourquoi avez-vous besoin d'une telle fonctionnalité? À moins que vous ne souhaitiez spécifiquement activer FTS pour votre application, c'est un moyen ... étrange ... d'accéder à vos données.

+1

Je voulais le faire une fois, lorsque la structure de la base de données était follement non intuitive et que je devais parcourir toutes les tables pour trouver d'où venait une valeur. Il a utilisé 2 curseurs, si je me souviens bien. pas beau. –

+0

Ceci est une demande des puissances ci-dessus. Mon patron m'a demandé de le faire. Il veut une recherche générique qui retournera n'importe quel objet de n'importe quel type basé sur un terme de recherche. – Ben

+1

@Ben Je suppose que vous avez essayé de lui expliquer à quel point cela était mauvais (réponse à @MrFox)? –

3

C'est probablement 'possible', mais la plupart des bases de données sont accessibles via le web ou le réseau, c'est donc une opération très coûteuse. Donc ça ressemble à un mauvais design.

Il y a aussi le problème des noms de table et de colonne, c'est probablement votre plus gros problème. Il est possible d'obtenir les noms de colonnes par la réflexion, mais je ne sais pas pour les noms de table:

foreach (PropertyInfo property in typeof(TEntity).GetProperties()) 
    yield return property.Name; 

modifier: @Ben, you'r droite mon erreur.

+0

Je pensais que LINQ couvert le "exécuter cette recherche sur le serveur et seulement retourner le nécessaire résultats en mémoire "partie pour moi? Ou ai-je tort sur celui-là? – Ben

+0

Je pensais suggérer quelque chose de similaire, enveloppé comme un assistant; mais cela me semble si diabolique qu'il ne fonctionnerait jamais "efficacement". Cela peut être le cas pour mettre la logique dans une seule procédure stockée et manipuler la création de l'objet vous-même. LINQ peut être trop "bavard" comme il est, sans encourager la "recherche partout" :) –

+0

LINQ to SQL utilisera des opérations en mémoire pour server une requête qu'il ne peut pas convertir en SQL - ou plutôt. Ce que vous décrivez ne peut même pas être défini dans SQL sans l'utilisation de curseurs, d'instructions longues ou de procédures stockées non documentées. –

2

Cela peut être fait mais ne sera pas joli. Il y a plusieurs solutions possibles.

1. Écrivez les requêtes pour chaque table vous-même et exécutez-les toutes dans votre méthode de requête.

var users = context.Users 
    .Where(x => x.FirstName.Contains(txt) || x.LastName.Contains(txt)) 
    .ToList(); 

var products = context.Products 
    .Where(x => x.ProductName.Contains(txt)); 

var result = user.Cast<Object>().Concat(products.Cast<Object>()); 

2. Fetch toutes les tables (pertinentes) en mémoire et effectuer la recherche en utilisant la réflexion. Moins de code à écrire payé avec un énorme impact sur les performances.

3. Construire les arbres d'expression pour les recherches en utilisant la réflexion. C'est probablement la meilleure solution, mais il est probablement difficile à réaliser.

4. Utilisez quelque chose conçu pour la recherche en texte intégral - par exemple une recherche de texte intégral intégrée à SQL Server ou à Apache Lucene.

Toute solution LINQ nécessitera (probablement) une requête par table ce qui impose un impact non négligeable sur les performances si vous avez plusieurs tables. Ici, il faut chercher une solution pour grouper ces requêtes en une seule. L'un de nos projets utilisant LINQ to SQL utilisait une bibliothèque pour les requêtes par lots, mais je ne sais pas quel était son nom et ce qu'il pouvait exactement faire parce que je travaillais la plupart du temps dans l'équipe frontale.

8

Demandez à votre patron ce qui suit:

« Boss, quand vous allez à la bibliothèque pour trouver un livre sur les widgets, marchez-vous jusqu'à la première étagère et commencer à lire tous les livres pour voir si elle est pertinente, ou utilisez-vous une sorte d'index pré-compilé que le bibliothécaire a utilement configuré pour vous, à l'avance?"

S'il dit: « Eh bien, j'utiliser l'index », alors vous avez besoin d'un index de texte intégral.

S'il dit: « Eh bien, je commencer à lire tous les livres, un par un », alors vous avez besoin d'un nouvel emploi, un nouveau patron, ou les deux :-)

+0

Haha, brillant :) – Ben

+0

@Ben Votre patron lit-il maintenant tous les livres de la bibliothèque? ;) –

+0

+1, inestimable ... –

0

possible mais de mon point de vue, il est déconseillé. Envisager d'avoir 1000K des dossiers de 100 tables. Lenteur vous pouvez le faire par Linq à SQL en faisant un Sp au niveau de la base de données et en appelant des entités.Il sera beaucoup plus rapide que celui que vous essayez d'atteindre =)

0

Réponse tardive, mais puisque je devais juste trouver quelque chose pour moi-même, voilà. J'ai écrit ce qui suit pour rechercher toutes les colonnes de toutes les tables pour une correspondance de chaîne. Ceci est lié à une tâche de recherche de données qui m'a été donnée pour trouver toutes les occurrences d'une correspondance de chaîne dans une base de données pesant environ 24Go. À cette taille, vous pouvez imaginer que l'utilisation de curseurs ou de requêtes à un seul thread sera plutôt lente et que la recherche dans toute la base de données prendrait beaucoup de temps. J'ai écrit la procédure stockée CLR suivante pour faire le travail côté serveur et renvoyer les résultats en XML, tout en forçant la parallélisation. C'est incroyablement rapide. Une recherche à l'échelle de la base de données sur la base de données AdventureWorks2017 standard se termine en moins de 2 secondes. Prendre plaisir!

Exemples d'utilisation:

Utilisation de tous les processeurs disponibles sur le serveur:

EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john michael' 

Limitation du serveur 4 threads simultanés:

EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john michael', @maxDegreeOfParallelism = 4 

Utilisation des opérateurs logiques à la recherche termes:

EXEC [dbo].[SearchAllTables] @valueSearchTerm = '(john or michael) and not jack', @tablesSearchTerm = 'not contact' 

Limiter la recherche aux noms de table et/ou les noms de colonnes contenant des termes de recherche:

EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john michael', @tablesSearchTerm = 'person contact', @columnsSearchTerm = 'address name' 

Limiter les résultats de la recherche à la première ligne de chaque table où les termes sont trouvés:

EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john michael', @getOnlyFirstRowPerTable = 1 

La limitation de la recherche au schéma renvoie uniquement uniquement la première ligne de chaque table:

EXEC [dbo].[SearchAllTables] @tablesSearchTerm = 'person contact' 

retour Seules les requêtes de recherche:

EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john michael', @tablesSearchTerm = 'person contact', @onlyOutputQueries = 1 

Capturer des résultats dans la table temporaire et tri:

CREATE TABLE #temp (Result NVARCHAR(MAX)); 
INSERT INTO #temp 
    EXEC [dbo].[SearchAllTables] @valueSearchTerm = 'john'; 
SELECT * FROM #temp ORDER BY Result ASC; 
DROP TABLE #temp; 

https://pastebin.com/RRTrt8ZN

Questions connexes