Select row based on dynmically created value - c#

I have a sql table below, value of column parameter and parameter value are dynamically created. The design below is cater for additional parameter being added in later stage. So I think using the parameter and parameter value as column is not ideal for such design.
|---------------------|------------------|------------------|
| Parameter | Parameter Value | Computers |
|---------------------|------------------|------------------|
| Phase | New | PC1 |
|---------------------|------------------|------------------|
| Phase | New | PC2 |
|---------------------|------------------|------------------|
| Phase | Redevelopment | PC3 |
|---------------------|------------------|------------------|
| Cost | High | PC1 |
|---------------------|------------------|------------------|
| Cost | High | PC2 |
|---------------------|------------------|------------------|
| Cost | Cost | PC3 |
|---------------------|------------------|------------------|
Given a scenario where a user search by Phase = "New" AND Cost = "High", it will result in PC1.
At this moment, I could think of is this:
SELECT *
FROM projectParameter
WHERE Parameter = 'Phase' AND Value = 'New' AND Parameter = 'Cost' AND Value = 'High'
Thanks in advance!

First, select all rows that match any part of your filtering.
Then aggregate all those rows to get one result per computer.
Then check each result to see if it contains all the required filtering contraints.
SELECT
Computers
FROM
yourTable
WHERE
(Parameter = 'Phase' AND ParameterValue = 'New')
OR
(Parameter = 'Cost' AND ParameterValue = 'High')
GROUP BY
Computers
HAVING
COUNT(*) = 2

From what I understand, it seems you want list of all computers where there is computer entry for both below conditions :
Parameter = 'Cost' AND Parameter Value = 'High'
Parameter = 'Phase' AND Parameter Value = 'New'
You can try below sql query to see it results your need :
SELECT t.computer
FROM table t
WHERE t.parameter = 'cost'
AND t.parameter_value = 'high'
AND EXISTS (
SELECT computer FROM table where computer=t.computer AND parameter = 'phase' AND parameter_value = 'new');

Related

Why is Dapper parameter in SQL 'WHERE' clause case sensitive?

Problem Description
When using a Dapper parameter in an SQL WHERE-clause, the parameter appears to be case sensitive. However, when I replace the Dapper parameter with a string literal, the WHERE-clause is no longer case sensitive. I have created a simple ASP.NET Core recipe example web API to help illustrate the problem.
In this recipe example, I am using a PostgreSQL database and want to query a recipe table to get a recipe by name. I have made the name column of type citext, which is a case-insensitive string type.
Database Table
A description of the recipe table:
+-------------+--------+-----------+----------+--------------------+
| Column | Type | Collation | Nullable | Default |
+-------------+--------+-----------+----------+--------------------+
| recipe_id | uuid | | not null | uuid_generate_v4() |
| name | citext | | not null | |
| description | text | | | |
+-------------+--------+-----------+----------+--------------------+
The contents of the recipe table are:
+--------------------------------------+--------------------+-----------------------------------------------------------+
| recipe_id | name | description |
+--------------------------------------+--------------------+-----------------------------------------------------------+
| 8f749e7a-e192-48df-91af-f319ab608212 | meatballs | balled up meat |
| f44c696f-a94a-4f17-a387-dd4d42f60ef8 | red beans and rice | yummy new orleans original |
| 82c5911b-feec-4854-9073-6a85ea793dc0 | pasta cereal | couscous and ground meat eaten with a spoon, like cereal! |
+--------------------------------------+--------------------+-----------------------------------------------------------+
Query Method
The RecipeController has a GetByName method that accepts the name parameter as part of the URI path. The GetByName method calls the GetByNameAsync method of the RecipeRepository class, which contains the SQL statement in question:
public async Task<Recipe> GetByNameAsync(string name)
{
string sql = $#"
SELECT *
FROM {nameof(Recipe)}
WHERE {nameof(Recipe)}.{nameof(Recipe.name)} = #{nameof(name)}";
using (IDbConnection connection = Open())
{
IEnumerable<Recipe> recipes = await connection.QueryAsync<Recipe>(sql, new {name});
return recipes.DefaultIfEmpty(new Recipe()).First();
}
}
Query Responses
If I wanted to query the meatballs recipe by name, and set the name parameter equal to "meatballs", I get the following response:
{
"recipe_id": "8f749e7a-e192-48df-91af-f319ab608212",
"name": "meatballs",
"description": "balled up meat"
}
Setting the name parameter equal to "Meatballs", I get the following response:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
"title": "Not Found",
"status": 404,
"traceId": "00-5e4e35d5cfec644fc117eaa96e854854-c0490c8ef510f3b1-00"
}
And finally, if I replace the Dapper name parameter with the string literal "Meatballs":
public async Task<Recipe> GetByNameAsync(string name)
{
string sql = $#"
SELECT *
FROM {nameof(Recipe)}
WHERE {nameof(Recipe)}.{nameof(Recipe.name)} = 'Meatballs'";
using (IDbConnection connection = Open())
{
IEnumerable<Recipe> recipes = await connection.QueryAsync<Recipe>(sql, new {name});
return recipes.DefaultIfEmpty(new Recipe()).First();
}
}
I get the following response:
{
"recipe_id": "8f749e7a-e192-48df-91af-f319ab608212",
"name": "meatballs",
"description": "balled up meat"
}
Why is this Dapper parameter forcing case-sensitivity? And how can I get around this?
Background
As Jeroen pointed out:
Presumably Dapper isn't doing any such thing and the same thing happens from any language where a parameter is passed as a regular string...
This indeed was not an issue with Dapper, but an issue with SQL data types. The name column in the recipeExample database does not know the incoming data type is supposed to be of type citext. Therefore, casting the incoming argument to citext is necessary.
As Jeroen also pointed out:
From what I gather Postgres also supports collations, and using a case-insensitive collation on a regular string type is likely to work without conversion of any kind.
Somehow I missed this, but the pgDocs even recommend considering nondeterministic collations instead of using the citext module. After reading up on localization, collations, and watching this YouTube video, I updated the recipe example web api to compare using the citext module with nondeterministic collations.
Database Update
First, I added the case_insensitive collation provided in the PostgreSQL documentation:
CREATE COLLATION case_insensitive (provider = icu, locale = 'und-u-ks-level2', deterministic = false);
Then, I updated the recipe table to have two name columns: name_1 of type text using the case_insensitive collation, and name_2 of type citext:
+-------------+--------+------------------+----------+--------------------+
| Column | Type | Collation | Nullable | Default |
+-------------+--------+------------------+----------+--------------------+
| recipe_id | uuid | | not null | uuid_generate_v4() |
| name_1 | text | case_insensitive | not null | |
| name_2 | citext | | not null | |
| description | text | | | |
+-------------+--------+------------------+----------+--------------------+
Indexes:
"recipe_pkey" PRIMARY KEY, btree (recipe_id)
"recipe_name_citext_key" UNIQUE CONSTRAINT, btree (name_2)
"recipe_name_key" UNIQUE CONSTRAINT, btree (name_1)
Next, I created three Postgres functions to test out the 'Meatballs' query:
The first function queries the name_1 column and takes a text argument
The second function queries the name_2 column and takes a text argument
The third function queries the name_2 column and takes a citext argument
CREATE FUNCTION getrecipe_name1_text(text) RETURNS recipe as $$
SELECT *
FROM recipe
WHERE recipe.name_1 = $1;
$$ LANGUAGE SQL;
CREATE FUNCTION getrecipe_name2_text(text) RETURNS recipe as $$
SELECT *
FROM recipe
WHERE recipe.name_2 = $1;
$$ LANGUAGE SQL;
CREATE FUNCTION getrecipe_name2_citext(citext) RETURNS recipe as $$
SELECT *
FROM recipe
WHERE recipe.name_2 = $1;
$$ LANGUAGE SQL;
Query Tests
Querying the name_1 column with text argument:
recipeexample=# SELECT * FROM getrecipe_name1_text('Meatballs');
+--------------------------------------+-----------+-----------+----------------+
| recipe_id | name_1 | name_2 | description |
+--------------------------------------+-----------+-----------+----------------+
| 8f749e7a-e192-48df-91af-f319ab608212 | meatballs | meatballs | balled up meat |
+--------------------------------------+-----------+-----------+----------------+
(1 row)
Querying the name_2 column with text argument:
recipeexample=# SELECT * FROM getrecipe_name2_text('Meatballs');
+-----------+--------+--------+-------------+
| recipe_id | name_1 | name_2 | description |
+-----------+--------+--------+-------------+
| | | | |
+-----------+--------+--------+-------------+
(1 row)
Querying the name_2 column with citext argument:
recipeexample=# SELECT * FROM getrecipe_name2_citext('Meatballs');
+--------------------------------------+-----------+-----------+----------------+
| recipe_id | name_1 | name_2 | description |
+--------------------------------------+-----------+-----------+----------------+
| 8f749e7a-e192-48df-91af-f319ab608212 | meatballs | meatballs | balled up meat |
+--------------------------------------+-----------+-----------+----------------+
(1 row)
Conclusion
If the citext module is used, arguments must be cast to citext when querying
If the case_insensitive collation is used, there will be performance penalties and pattern matching operations are not possible

Remove DataColumn from DataTable with Data

I have Datatable with Data. I called my postgres data base and get DataTable.
// get the new data from postgres
var dataTable = pg.GetDataTable("SELECT * FROM " + '"'+table+'"');
Now I need to remove specific Column. assume that there are 5 columns with data.
ID | Name | Address | Mobile | Home
_______________________________________________
1 | A |AAA | 123 | 345
2 | B |BBB | 234 | 456
3 | C |CCC | 345 | 567
So I need to remove "Home " DataColumn and Recreate this DataTable as following
ID | Name | Address | Mobile
____________________________________
1 | A |AAA | 123
2 | B |BBB | 234
3 | C |CCC | 345
How can I do this ?
Appreciate your comments.
Thanks
Just specify the columns you need explicitly rather than selecting unneeded columns and removing them afterwards:
SELECT "Id", "Name", "Address", "Mobile"
Using SELECT * is bad manners because it makes your contract with database unstable - should column configuration change you will get unpredictable result.
You just need to use the method DataTable.Columns.Remove(string name):
dataTable.Columns.Remove("Home");
Then the table doesn't contain this column anymore and all rows' data in this column is thrown away. However, omit this column in the first place and list the desired columns:
var dataTable = pg.GetDataTable("SELECT ID, Name, Address, Mobile FROM " + '"'+ table +'"');
Consider using
dataTable.Columns.Remove("ColumnName")
or
dataTable.Columns.RemoveAt(ColumnNumber)

Selecting records with max version

I have a table as follows:
ConfigID | VersionNo | ObjectType
ConfigID and VersionNo constitute the unique key.
I want to be able to select the record with the highest VersionNo for each configID based on an object type.
I have tried
configs = (from config in configRepository.FindBy(x => x.ObjectType.Equals(typeof(Node).ToString(), StringComparison.InvariantCultureIgnoreCase))
group config by config.ConfigID into orderedConfigs
select orderedConfigs.OrderBy(x => x.ConfigID).ThenByDescending(x => x.VersionNo).First());
EDIT: I must add that the FindBy is basically just a where clause.
But I am getting no results. Please help me with this.
EDIT:
The data in the table could look like:
3fa1e32a-e341-46fd-885d-8f06ad0caf2e | 1 | Sybrin10.Common.DTO.Node
3fa1e32a-e341-46fd-885d-8f06ad0caf2e | 2 | Sybrin10.Common.DTO.Node
51d2a6c7-292d-42fc-ae64-acd238d26ccf | 3 | Sybrin10.Common.DTO.Node
51d2a6c7-292d-42fc-ae64-acd238d26ccf | 4 | Sybrin10.Common.DTO.Node
8dbf7a33-441f-40bc-b594-e34c5a2c3f51 | 1 | Some Other Type
91413e73-4997-4643-b7d2-e4c208163c0d | 1 | Some Other Type
From this I would only want to retrieve the second and fourth records as they have the highest version numbers for the configID and are of the required type.
Not sure if 100% works because writing out of VS :) but idea should be good
var configs = configRepository.Where(x=>x.ObjectType==typeof(Node).ToString());
var grouped = configs.GroupBy(x=>x.ConfigId);
var a = grouped.select(x=>x.OrderByDescending(y=>y.VersionNo).First());
It looks LINQ sql to but in pure SQL i can write the query like this
SELECT ConfigID ,MAX(VersionNo ) FROM CUSTOMIZE_COLUMNS_DETAIL WHERE
ObjectType = 'objectType' GROUP BY ConfigID
I have tried to replicate the scenario in sql , might by useful to you, THanks

How do a make a query to count which string appears the most in a certain column?

I'm making a program and I need to make a query to the database asking for the string that appears most often in a given column. In this example, its "stringONE".
----------------------------
| ID | Column (string) |
----------------------------
| 1 | stringONE |
----------------------------
| 2 | stringTWO |
----------------------------
| 3 | stringONE |
----------------------------
| 4 | stringONE |
----------------------------
Now I need to take the name of the string that appears the most and put it into a variable string, for example:
string most_appeared_string = sql.ExecuteScalar();
Also, what happens if there is no string that appears the most, rather 2 or more strings that appear the same amount of times, like this:
----------------------------
| ID | Column (string) |
----------------------------
| 1 | stringONE |
----------------------------
| 2 | stringTWO |
----------------------------
| 3 | stringTWO |
----------------------------
| 4 | stringONE |
----------------------------
Thanks ahead.
#KeithS
Do you have an sql-server version of the query because I'm getting some errors when trying it there. Here's a table example of what I'd like to do precisely.
------------------------------------------------
| ID | column1 (string) | author (string) |
------------------------------------------------
| 1 | string-ONE | John |
------------------------------------------------
| 2 | string-TWO | John |
------------------------------------------------
| 3 | string-ONE | Martin |
------------------------------------------------
| 4 | string-ONE | John |
------------------------------------------------
SELECT TOP (1) column1, COUNT(*) FROM table WHERE author='John' ORDER BY ID
It should return "string-ONE" since it appears the most (2) times for the author John. When trying the query in MS-SQL Management Studio though, this is the error I'm getting:
Column 'table.column1' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Nevermind the edit. Thank you.
This is a pretty easy query (in T-SQL at least):
select top 1 Column, Count(*) from Table group by Column order by Count(*) desc
ExecuteScalar, by an implementation detail, will return the string value because it's the first column of the only row in the result set, even though there are two columns. You could also use ExecuteReader to access the number of times that string occurs.
select top (1) SomeCol, count(*) as Row_Count
from YourTable
group by SomeCol
order by Row_Count desc
Also, what happens if there is no string that appears the most, rather
2 or more strings that appear the same amount of times, like this:
In that case, using the above query, you will get one arbitrary row. You can add with ties to get all rows that has the same highest value.
select top (1) with ties SomeCol, count(*) as Row_Count
from YourTable
group by SomeCol
order by Row_Count desc
SELECT max(counted) AS max_counted FROM (
SELECT count(*) AS counted FROM counter GROUP BY date
)
This could do the trick

Query tabels to sort sums across rows and count based on a value in LINQ

Here is my problem. i have 3-5 persons that is going to set a grade on one person and they use their own individual row to do so, and what I'm having trouble to do is to sum and average the grade from individual data across multiple rows on the same table.
in the select new statement i have made a pseudo answer of what i want
var users = from workRew in db.Reviews
select new
{
UserID = workRew.UserID.DistinctOfSomeSort
AvgGrade = workRew.Grade.Sum/CountOfSomeSort
};
Here i a illustration.
So if i have this table
| SomeID | UserID | Grade |
| 1 | 2 | 3 |
| 2 | 3 | 1 |
| 3 | 2 | 1 |
And this is the output i want from the LINQ query on the above (In theory ateast)
| UserID | AvgGrade |
| 2 | 2 |
| 3 | 1 |
EDIT: Simplified the whole case, to a great extent.
It should look something like this fragment:
group by user.UserID
select new
{
User = user.UserID
TotGradeCount = workRew.Grade.Sum()
Graders = workRew.Grade.Count()
}

Categories

Resources