Использование LINQ to SQL для поиска по всей базе данных - PullRequest
3 голосов
/ 07 июля 2010

Возможно ли с помощью LINQ to SQL выполнить поиск строки во всей базе данных (очевидно, только в тех частях, которые отображаются в файле .dbml)? Я пытаюсь написать функцию, которая будет принимать строку «Термин поиска» и искать все сопоставленные сущности и возвращать список (объекта), который может содержать смесь сущностей, т.е. если у меня есть таблица «Foo» и таблица » Bar »и найдите« wibble », если в« Foo »есть строка, а в« Bar »есть строка, содержащая« wibble », я хотел бы вернуть List (Of Object), содержащий объект« Foo »и Бар "объект. Возможно ли это?

Ответы [ 7 ]

8 голосов
/ 07 июля 2010

Спросите у своего босса следующее:

"Босс, когда вы идете в библиотеку, чтобы найти книгу о виджетах, вы подходите к первой полке и начинаете читать каждую книгу, чтобы увидеть, уместна ли она, или вы используете какой-то предварительно составленный индекс что библиотекарь заранее настроил для вас? "

Если он говорит: «Ну, я бы использовал индекс», тогда вам нужен полнотекстовый индекс.

Если он скажет: «Ну, я бы начал читать каждую книгу, одну за другой», тогда вам нужна новая работа, новый босс или оба: -)

7 голосов
/ 07 июля 2010

LINQ to SQL, ORM в целом, даже SQL плохо подходит для такого запроса.Вы описываете полнотекстовый поиск, поэтому вам следует использовать функцию полнотекстового поиска SQL Server. Полнотекстовый поиск доступен во всех версиях и выпусках с 2000 года, включая SQL Server Express.Вам нужно создать каталог FTS и написать запросы, которые используют функции CONTAINS, FREETEXT в ваших запросах.

Зачем вам такая функциональность?Если вы специально не хотите включить FTS в своем приложении, это ... странный ... способ доступа к вашим данным.

3 голосов
/ 07 июля 2010

Это, вероятно, «возможно», но доступ к большинству баз данных осуществляется через Интернет или сеть, поэтому это очень дорогая операция.Это звучит как плохой дизайн.

Также существует проблема с именами таблиц и столбцов, это, вероятно, ваша самая большая проблема.Можно получить имена столбцов с помощью отражения, но я не знаю для имен таблиц:

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

edit: @Ben, вы исправите мою ошибку.

2 голосов
/ 07 июля 2010

Это можно сделать, но не будет красиво. Есть несколько возможных решений.

1. Напишите запросы для каждой таблицы самостоятельно и выполните их все в вашем методе запроса.

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. Получить все (соответствующие) таблицы в память и выполнить поиск, используя отражение. Меньше кода для написания платит с огромным влиянием на производительность.

3. Построить деревья выражений для поиска, используя отражение. Это, вероятно, лучшее решение, но его сложно реализовать.

4. Используйте что-то, предназначенное для полнотекстового поиска - например, полнотекстовый поиск, интегрированный в SQL Server или Apache Lucene.

Для всех решений LINQ (вероятно) потребуется один запрос на таблицу, что приводит к значительному снижению производительности, если у вас много таблиц. Здесь следует искать решение для объединения этих запросов в один. В одном из наших проектов, использующих LINQ to SQL, использовалась библиотека для пакетных запросов, но я не знаю, как ее называли и что именно она могла сделать, потому что большую часть времени я работал в команде переднего плана.

0 голосов
/ 28 ноября 2018

Я закончил тем, что написал этот маленький пользовательский самоцвет (находит все подходящие записи по заданному поисковому запросу):

namespace SqlServerMetaSearchScan
{
    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.SqlClient;
    using System.IO;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    using System.Threading;
    using System.Xml;

public class Program
{
    #region Ignition
    public static void Main(string[] args)
    {
        // Defaulting
        SqlConnection connection = null;

        try
        {               
            // Questions
            ColorConsole.Print("SQL Connection String> ");
            string connectionString = Console.ReadLine();
            ColorConsole.Print("Search Term (Case Ignored)> ");
            string searchTerm = Console.ReadLine();
            ColorConsole.Print("Skip Databases (Comma Delimited)> ");
            List<string> skipDatabases = Console.ReadLine().Split(',').Where(item => item.Trim() != string.Empty).ToList();

            // Search
            connection = new SqlConnection(connectionString);
            connection.Open();

            // Each database
            List<string> databases = new List<string>();
            string databasesLookup = "SELECT name FROM master.dbo.sysdatabases";
            SqlDataReader reader = new SqlCommand(databasesLookup, connection).ExecuteReader();
            while (reader.Read())
            {
                // Capture
                databases.Add(reader.GetValue(0).ToString());
            }

            // Build quintessential folder
            string logsDirectory = @"E:\Logs";
            if (!Directory.Exists(logsDirectory))
            {
                // Build
                Directory.CreateDirectory(logsDirectory);
            }
            string baseFolder = @"E:\Logs\SqlMetaProbeResults";
            if (!Directory.Exists(baseFolder))
            {
                // Build
                Directory.CreateDirectory(baseFolder);
            }

            // Close reader
            reader.Close();                

            // Sort databases
            databases.Sort();

            // New space
            Console.WriteLine(Environment.NewLine + " Found " + databases.Count + " Database(s) to Scan" + Environment.NewLine);

            // Deep scan
            foreach (string databaseName in databases)
            {
                // Skip skip databases
                if (skipDatabases.Contains(databaseName))
                {
                    // Skip 
                    continue;
                }

                // Select the database
                new SqlCommand("USE " + databaseName, connection).ExecuteNonQuery();

                // Table count
                int tablePosition = 1;

                try
                {
                    // Defaulting
                    List<string> tableNames = new List<string>();

                    // Schema examination
                    DataTable table = connection.GetSchema("Tables");

                    // Query tables
                    string tablesLookup = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES";
                    using (SqlDataReader databaseReader = new SqlCommand(tablesLookup, connection).ExecuteReader())
                    {
                        // Get data
                        while (databaseReader.Read())
                        {
                            // Push
                            if (databaseReader.GetValue(0).ToString().Trim() != string.Empty)
                            {
                                tableNames.Add(databaseReader.GetValue(0).ToString());
                            }
                        }

                        // Bail
                        databaseReader.Close();
                    }

                    // Sort
                    tableNames.Sort();                        

                    // Cycle tables
                    foreach (string tableName in tableNames)
                    {
                        // Build data housing
                        string databasePathName = @"E:\Logs\\SqlMetaProbeResults" + databaseName;
                        string tableDirectoryPath = @"E:\Logs\SqlMetaProbeResults\" + databaseName + @"\" + tableName;

                        // Count first
                        int totalEntityCount = 0;
                        int currentEntityPosition = 0;                            
                        string countQuery = "SELECT count(*) FROM " + databaseName + ".dbo." + tableName;
                        using (SqlDataReader entityCountReader = new SqlCommand(countQuery, connection).ExecuteReader())
                        {
                            // Query count
                            while (entityCountReader.Read())
                            {
                                // Capture
                                totalEntityCount = int.Parse(entityCountReader.GetValue(0).ToString());
                            }

                            // Close
                            entityCountReader.Close();
                        }

                        // Write the objects into the houseing
                        string jsonLookupQuery = "SELECT * FROM " + databaseName + ".dbo." + tableName;
                        using (SqlDataReader tableReader = new SqlCommand(jsonLookupQuery, connection).ExecuteReader())
                        {                                                
                            // Defaulting
                            List<string> fieldValueListing = new List<string>();

                            // Read continue
                            while (tableReader.Read())
                            {                                   
                                // Increment
                                currentEntityPosition++;

                                // Defaulting
                                string identity = null;

                                // Gather data
                                for (int i = 0; i < tableReader.FieldCount; i++)
                                {
                                    // Set
                                    if (tableReader.GetName(i).ToUpper() == "ID")
                                    {
                                        identity = tableReader.GetValue(0).ToString();
                                    }
                                    else
                                    {
                                        // Build column data entry
                                        string thisColumn = tableReader.GetValue(i) != null ? "'" + tableReader.GetValue(i).ToString().Trim() + "'" : string.Empty;

                                        // Piece
                                        fieldValueListing.Add(thisColumn);
                                    }
                                }

                                // Path-centric
                                string explicitIdentity = identity ?? Guid.NewGuid().ToString().Replace("-", string.Empty).ToLower();
                                string filePath = tableDirectoryPath + @"\" + "Obj." + explicitIdentity + ".json";
                                string reStringed = JsonConvert.SerializeObject(fieldValueListing, Newtonsoft.Json.Formatting.Indented);
                                string percentageMark = ((double)tablePosition / (double)tableNames.Count * 100).ToString("#00.0") + "%";                                                      
                                string thisMarker = Guid.NewGuid().ToString().Replace("-", string.Empty).ToLower();
                                string entityPercentMark = string.Empty;
                                if (totalEntityCount != 0 && currentEntityPosition != 0)
                                {
                                    // Percent mark
                                    entityPercentMark = ((double)currentEntityPosition / (double)totalEntityCount * 100).ToString("#00.0") + "%";
                                }

                                // Search term verify
                                if (searchTerm.Trim() != string.Empty)
                                {
                                    // Search term scenario
                                    if (reStringed.ToLower().Trim().Contains(searchTerm.ToLower().Trim()))
                                    {
                                        // Lazy build
                                        if (!Directory.Exists(tableDirectoryPath))
                                        {
                                            // Build
                                            Directory.CreateDirectory(tableDirectoryPath);
                                        }

                                        // Has the term
                                        string idMolding = identity == null || identity == string.Empty ? "No Identity" : identity;
                                        File.WriteAllText(filePath, reStringed);
                                        ColorConsole.Print(percentageMark + " => " + databaseName + "." + tableName + "." + idMolding + "." + thisMarker + " (" + entityPercentMark + ")", ConsoleColor.Green, ConsoleColor.Black, true);
                                    }
                                    else
                                    {
                                        // Show progress                                            
                                        string idMolding = identity == null || identity == string.Empty ? "No Identity" : identity;
                                        ColorConsole.Print(percentageMark + " => " + databaseName + "." + tableName + "." + idMolding + "." + thisMarker + " (" + entityPercentMark + ")", ConsoleColor.Yellow, ConsoleColor.Black, true);
                                    }
                                }
                            }

                            // Close
                            tableReader.Close();
                        }

                        // Increment
                        tablePosition++;
                    }                        
                }
                catch (Exception err)
                {
                    ColorConsole.Print("DB.Tables!: " + err.Message, ConsoleColor.Red, ConsoleColor.White, false);                        
                }
            }
        }
        catch (Exception err)
        {
            ColorConsole.Print("KABOOM!: " + err.ToString(), ConsoleColor.Red, ConsoleColor.White, false);                
        }
        finally
        {
            try { connection.Close(); }
            catch { }
        }


        // Await
        ColorConsole.Print("Done.");
        Console.ReadLine();
    }
    #endregion

    #region Cores
    public static string GenerateHash(string inputString)
    {
        // Defaulting
        string calculatedChecksum = null;

        // Calculate
        SHA256Managed checksumBuilder = new SHA256Managed();
        string hashString = string.Empty;
        byte[] hashBytes = checksumBuilder.ComputeHash(Encoding.ASCII.GetBytes(inputString));
        foreach (byte theByte in hashBytes)
        {
            hashString += theByte.ToString("x2");
        }
        calculatedChecksum = hashString;

        // Return
        return calculatedChecksum;
    }
    #endregion


    #region Colors
    public class ColorConsole
    {
        #region Defaulting
        public static ConsoleColor DefaultBackground = ConsoleColor.DarkBlue;
        public static ConsoleColor DefaultForeground = ConsoleColor.Yellow;
        public static string DefaultBackPorch = "                                                                              ";
        #endregion

        #region Printer Cores
        public static void Print(string phrase)
        {
            // Use primary
            Print(phrase, DefaultForeground, DefaultBackground, false);
        }
        public static void Print(string phrase, ConsoleColor customForecolor)
        {
            // Use primary
            Print(phrase, customForecolor, DefaultBackground, false);
        }
        public static void Print(string phrase, ConsoleColor customBackcolor, bool inPlace)
        {
            // Use primary
            Print(phrase, DefaultForeground, customBackcolor, inPlace);
        }
        public static void Print(string phrase, ConsoleColor customForecolor, ConsoleColor customBackcolor)
        {
            // Use primary
            Print(phrase, customForecolor, customBackcolor, false);
        }
        public static void Print(string phrase, ConsoleColor customForecolor, ConsoleColor customBackcolor, bool inPlace)
        {
            // Capture settings
            ConsoleColor captureForeground = Console.ForegroundColor;
            ConsoleColor captureBackground = Console.BackgroundColor;

            // Change colors
            Console.ForegroundColor = customForecolor;
            Console.BackgroundColor = customBackcolor;

            // Write
            if (inPlace)
            {
                // From beginning of this line + padding
                Console.Write("\r" + phrase + DefaultBackPorch);
            }
            else
            {
                // Normal write
                Console.Write(phrase);
            }

            // Revert
            Console.ForegroundColor = captureForeground;
            Console.BackgroundColor = captureBackground;
        }
        #endregion
    }
    #endregion
}
}
0 голосов
/ 24 сентября 2017

Поздний ответ, но так как я просто должен был что-то придумать для себя, здесь идет.Я написал следующее, чтобы найти во всех столбцах всех таблиц совпадение строк.Это связано с задачей анализа данных, которая была дана мне, чтобы найти все совпадения строк в базе данных весом около 24 ГБ.При таком размере вы можете себе представить, что использование курсоров или однопоточных запросов будет довольно медленным, а поиск по всей базе данных займет много времени.Я написал следующую хранимую процедуру CLR, чтобы выполнить работу для меня на стороне сервера и вернуть результаты в XML, в то же время форсируя распараллеливание.Это впечатляюще быстро.Поиск по всей базе данных в стандартной базе данных AdventureWorks2017 выполняется менее чем за 2 секунды.Наслаждайтесь!

Примеры использования:

Использование всех доступных процессоров на сервере:

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

Ограничение сервера до 4 одновременных потоков:

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

Использование логических операторов в терминах поиска:

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

Ограничение поиска именами таблиц и / или имен столбцов, содержащих некоторые условия поиска:

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

Ограничение результатов поиска первой строкой каждой таблицы, в которой найдены термины:

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

Ограничение поиска только схемой автоматически возвращает только первую строку для каждой таблицы:

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

Возвращать только поисковые запросы:

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

Захват результатов во временную таблицу и сортировка:

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

0 голосов
/ 01 декабря 2010

Возможно, но, с моей точки зрения, не рекомендуется. Рассмотрим 1000К записей из 100 таблиц. Низкая производительность Вы можете сделать это с помощью Linq to SQL , сделав Sp на уровне базы данных и вызывая объекты. Это будет намного быстрее, чем тот, который вы пытаетесь достичь =)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...