{"id":21375252,"url":"https://github.com/karenpayneoregon/dyynamic-sql-where-in","last_synced_at":"2025-07-13T09:32:45.259Z","repository":{"id":65582947,"uuid":"445846542","full_name":"karenpayneoregon/dyynamic-sql-where-in","owner":"karenpayneoregon","description":"Provides methods to generate dynamic  WHERE IN clauses for databases","archived":false,"fork":false,"pushed_at":"2024-01-10T18:04:04.000Z","size":633,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-28T17:26:05.711Z","etag":null,"topics":["csharp","csharp-core","sql","sql-server"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karenpayneoregon.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-01-08T14:49:02.000Z","updated_at":"2024-03-13T03:32:22.000Z","dependencies_parsed_at":"2024-01-10T19:27:38.422Z","dependency_job_id":"24341b31-a0cb-4bc4-ac56-7e0f0b21fc09","html_url":"https://github.com/karenpayneoregon/dyynamic-sql-where-in","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fdyynamic-sql-where-in","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fdyynamic-sql-where-in/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fdyynamic-sql-where-in/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fdyynamic-sql-where-in/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karenpayneoregon","download_url":"https://codeload.github.com/karenpayneoregon/dyynamic-sql-where-in/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225578172,"owners_count":17491268,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","csharp-core","sql","sql-server"],"created_at":"2024-11-22T09:09:26.972Z","updated_at":"2024-11-22T09:09:27.594Z","avatar_url":"https://github.com/karenpayneoregon.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dynamic WHERE IN (C#)\n\nThis article provides methods for writing [SQL WHERE IN](https://docs.microsoft.com/en-us/sql/t-sql/language-elements/in-transact-sql?view=sql-server-2017) conditions in `C#` dynamically for `SQL-Server` tables using `SqlClient` data provider. `WHERE IN` condition are used to assist for an alternative to using `OR` conditions in a `SELECT` and `DELETE` statement are most common. Common reasons in a .NET solution, one example would be to allow a user to filter on customers by multiple countries while another example may be to allow a user to remove multiple customers. \n\nBoth methods may start with a multi-select list or multi-select checkbox control/input dependent on the project type e.g. ASP.NET, WPF or Windows Forms etc.\n\n## Running code samples\n\n- ~~Open BaseSqlServerConnections class in BaseLibrary project and change DatabaseServer from KARENS-PC to your server name or .\\SQLEXPRESS.~~\n- Open script.sql located `DatabaseScripts folder` off the Solution root folder.\n- Line 7 and line 8, check the path for FILENAME, ensure it points to your SQL-Server path, if different than alter the path.\n- Run the script in Visual Studio or SSMS (SQL-Server Management Studio).\n- In Visual Studio open Test Explorer.\n- Build the solution.\n- Run the test from Test Explorer or run each test one at a time.\n\n## NuGet package\n\nFor .NET Core see NuGet package [WhereInUtilityLibrary](https://www.nuget.org/packages/WhereInUtilityLibrary/)\n\n## SQL-Server/OleDb versions\n\nThere are two versions, one for `SQL-Server` and one for `OleDb/MS-Access`. Currently the OleDb version has limited testing. \n\n### Data access \n\nIs done using data provider connection and command objects, not Entity Framework.\n\n#### Examples included \n\nWindows forms\n\n![img](SimpleExamples/assets/whereInForm1.png)\n\nWPF\n\n![img](assets/figure5.png)\n\nExample where this might be used also for web solutions.\n\n![img](assets/figure1.png)\n\n![img](assets/figure3.png)\n\n![img](assets/figure4.png)\n\n## IN vs OR\n\nIn short both return the same results yet when there are many conditions with `OR` syntax one must repeat the field name for each condition and also means when in a complex statement more to maintain.\n\n```sql\nSELECT  id\nFROM    dbo.Company\nWHERE   CompanyName = 'FaceBook'\n        OR CompanyName = 'Macy''s';\n```\nWhile with `IN` the field name is used once, less to maintain.\n\n```sql\nSELECT  id\nFROM    dbo.Company\nWHERE   CompanyName IN ( 'FaceBook', 'Macy''s' );\n```\n\nTo dynamically create a WHERE IN condition the type of parameter needs to be considered along with attention needs to be paid for apostrophes in string values. \n\nThis means when creating parameters Command.Parameters.AddWithValue will not work properly as AddWithValue can take a numeric and convert to a string for instance. This means Command.Parameters.Add needs to be used and set [SqlDbType](https://docs.microsoft.com/en-us/dotnet/api/system.data.sqldbtype?view=net-6.0) property of a parameter to the proper type. \n\nFor this the following method GetDbType accepts a generic type and returns the proper SqlDbType.\n\n```csharp\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\n\nnamespace SqlCoreUtilityLibrary.Classes\n{\n    public static class SqlTypeHelper\n    {\n        private static readonly Dictionary\u003cType, SqlDbType\u003e TypeMap;\n\n        // Create and populate the dictionary in the static constructor\n        static SqlTypeHelper()\n        {\n            TypeMap = new Dictionary\u003cType, SqlDbType\u003e\n            {\n                [typeof(string)] = SqlDbType.NVarChar,\n                [typeof(char[])] = SqlDbType.NVarChar,\n                [typeof(byte)] = SqlDbType.TinyInt,\n                [typeof(short)] = SqlDbType.SmallInt,\n                [typeof(int)] = SqlDbType.Int,\n                [typeof(long)] = SqlDbType.BigInt,\n                [typeof(byte[])] = SqlDbType.Image,\n                [typeof(bool)] = SqlDbType.Bit,\n                [typeof(DateTime)] = SqlDbType.DateTime2,\n                [typeof(DateTimeOffset)] = SqlDbType.DateTimeOffset,\n                [typeof(decimal)] = SqlDbType.Money,\n                [typeof(float)] = SqlDbType.Real,\n                [typeof(double)] = SqlDbType.Float,\n                [typeof(TimeSpan)] = SqlDbType.Time\n            };\n\n            /* not in above then added them */\n        }\n\n        /// \u003csummary\u003e\n        /// Get SqlDbType for givenType\n        /// \u003c/summary\u003e\n        /// \u003cparam name=\"giveType\"\u003e\u003c/param\u003e\n        /// \u003creturns\u003e\u003csee cref=\"SqlDbType\"/\u003e\u003c/returns\u003e\n        public static SqlDbType GetDbType(Type giveType)\n        {\n            // Allow nullable types to be handled\n            giveType = Nullable.GetUnderlyingType(giveType) ?? giveType;\n\n            if (TypeMap.ContainsKey(giveType))\n            {\n                return TypeMap[giveType];\n            }\n\n            throw new ArgumentException($\"{giveType.FullName} is not a supported .NET class\");\n        }\n    }\n}\n```\n\nGetDbType (above) is called by the following language extension method where T may be one of the types TypesMap Dictionary above.\n\n```csharp\npublic static void AddParamsToCommand\u003cT\u003e(this SqlCommand cmd, string pPrefix, IEnumerable\u003cT\u003e parameters)\n{\n    var parameterValues = parameters.Select((paramText) =\u003e paramText.ToString()).ToArray();\n\n    var parameterNames = parameterValues.\n        Select((paramText, paramNumber) =\u003e $\"@{pPrefix}{paramNumber}\").ToArray();\n\n    for (int index = 0; index \u003c parameterNames.Length; index++)\n    {\n        cmd.Parameters.Add(new SqlParameter()\n        {\n            ParameterName = parameterNames[index],\n            SqlDbType = SqlTypeHelper.GetDbType(typeof(T)),\n            Value = parameterValues[index]\n        });\n    }\n}\n```\n\nThe following takes a partial `WHERE IN` condition e.g. `SELECT` Id `FROM` Customers `WHERE` Country `IN` ({0}). {0} will be replaced with one parameter for each value passed in from the last parameter. pPrefix prefixes each parameter name e.g. if the prefix was country and there were three country names we end up with `@country1`, `@country2`, `@country3`.\n\n```csharp\npublic static string BuildInClause\u003cT\u003e(string partialClause, string pPrefix, IEnumerable\u003cT\u003e parameters)\n{\n    string[] parameterNames = parameters.Select((paramText, paramNumber) =\u003e $\"@{pPrefix}{paramNumber}\").ToArray();\n\n    var inClause = string.Join(\",\", parameterNames);\n    var whereInClause = string.Format(partialClause.Trim(), inClause);\n\n    return whereInClause;\n\n}\n```\n\n## Example\n\nUsing the following table\n\n```sql\nCREATE TABLE dbo.Company\n    (\n      id INT IDENTITY(1, 1)\n             NOT NULL ,\n      CompanyName NVARCHAR(MAX) NULL ,\n      CONSTRAINT PK_Company PRIMARY KEY CLUSTERED ( id ASC )\n        WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,\n               IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,\n               ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY]\n    )\nON  [PRIMARY] TEXTIMAGE_ON [PRIMARY];\n```\n\nUsing a multi-select control with company names displayed and for each item in the control a primary key, pass in a list of keys or an array to the method to return records that have the keys passed in. To demonstrate this a unit test is perfect.\n\nIn the following test method primary keys are passed to a function. The variable expectedResults contain the company names expected to be returned for the keys. In a real application more than a list of strings would be returned but all we need to validate are names of companies since if these are correct the same will hold true of records or a list of a class.\n\n```csharp\n[TestMethod]\npublic void IntWhereConditions()\n{\n    // arrange\n    var expectedResults = new List\u003cstring\u003e()\n    {\n        \"Apple\",\n        \"FaceBook\",\n        \"Karen's Coffee\"\n    };\n \n    // act\n    var results = GetByPrimaryKeys(new List\u003cint\u003e() { 2,4,5 });\n \n    // assert\n    Assert.IsTrue(expectedResults.SequenceEqual(results));\n}\n```\n\nThe following method takes each parameter, builds a parameter in cmd, the `SqlCommand` then sets values for each parameter.\n\n```csharp\npublic List\u003cstring\u003e GetByPrimaryKeys(List\u003cint\u003e pIdentifiers)\n{\n    mHasException = false;\n    var customerList = new List\u003cstring\u003e();\n \n    using (var cn = new SqlConnection() { ConnectionString = ConnectionString })\n    {\n \n        using (var cmd = new SqlCommand() { Connection = cn})\n        {\n            // create one parameter for each key in pIdentifiers\n            cmd.CommandText = SqlWhereInParamBuilder.BuildInClause(\n                \"SELECT CompanyName FROM dbo.Company WHERE id IN ({0})\", \"CompId\", pIdentifiers);\n \n            // populate each parameter with values from pIdentifiers\n            cmd.AddParamsToCommand(\"CompId\", pIdentifiers);\n \n            try\n            {\n                cn.Open();\n                var reader = cmd.ExecuteReader();\n \n                if (reader.HasRows)\n                {\n                    while (reader.Read())\n                    {\n                        customerList.Add(reader.GetString(0));\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                mHasException = true;\n                mLastException = ex;\n            }\n        }\n    }\n \n    return customerList;\n \n}\n```\n\nSince `SqlWhereInParamBuilder.BuildInClause` figured out parameter types no matter the type passed in you can pass in strings rather than a int as per the above example e.g.\n\n```csharp\npublic List\u003cint\u003e GetCustomersKeysBack(List\u003cstring\u003e pNames)\n{\n    mHasException = false;\n    var customerList = new List\u003cint\u003e();\n \n    using (var cn = new SqlConnection() { ConnectionString = ConnectionString })\n    {\n        using (var cmd = new SqlCommand() { Connection = cn })\n        {\n            // Create a parameter for each value in pNames\n            cmd.CommandText = SqlWhereInParamBuilder.BuildInClause(\n                \"SELECT id FROM dbo.Company WHERE CompanyName IN ({0})\", \"CompName\", pNames);\n \n            // populate parameters created in BuildInClause\n            cmd.AddParamsToCommand(\"CompName\", pNames);\n \n            try\n            {\n                cn.Open();\n \n                // After running this test click on the test method calling\n                // this method and select \"output\" to view the SELECT statement.\n                Console.WriteLine(cmd.ActualCommandText());\n \n                var reader = cmd.ExecuteReader();\n                if (reader.HasRows)\n                {\n                    while (reader.Read())\n                    {\n                        customerList.Add(reader.GetInt32(0));\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                mHasException = true;\n                mLastException = ex;\n            }\n        }\n    }\n \n    return customerList;\n}\n```\n\nDates can be done also yet they are more complex depending on if there is a time aspect of the date. Images and binary types also present issues too so these methods work best with strings and numeric types.\n\n## Usage\n\nInclude project SqlUtilityLibrary into your Visual Studio solution, add a reference to your project for SqlUtilityLibrary and write statements as per below.\n\n```\nCreate the SELECT [field list] \nAppend WHERE IN ({0})\n```\n\nNote parenesis then curly brace followed by 0 then close curly brace then close parenesis.\n\n![img](assets/figure2.png)\n\n\n## How to use in your project\n\n- Add the project from the code sample named SqlUtilityLibrary into your Visual Studio solution.\n- Add a reference to the library in your project.\n- Write SQL statements as per above.\n\n\n\n\n\n# Original source\n\nI wrote the following under `.NET Framework 4.6`. This version using `.NET Core 5` along with refactors to lose code with the same functionality as the original code.\n\n[SQL-Server dynamic C#: Dynamic WHERE IN conditions in C#for SQL Server](https://social.technet.microsoft.com/wiki/contents/articles/51874.sql-server-dynamic-c-dynamic-where-in-conditions-in-c-for-sql-server.aspx)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarenpayneoregon%2Fdyynamic-sql-where-in","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarenpayneoregon%2Fdyynamic-sql-where-in","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarenpayneoregon%2Fdyynamic-sql-where-in/lists"}