I've got an application which generates a number of SQL statements, whose selection fields all contain AS clauses, like so:
SELECT TOP 200 [customer_name] AS [Customer Name], [customer_age] AS [Customer Age], AVG([customer_age]) AS 'Average Customer Age' FROM [tb_customers] GROUP BY [customer_age]
My statements will always be in that format. My task is to parse them so that "TOP 200" is removed, as well as all the AS clauses except for aggregates. In other words, I would want to parse the statements and in that case it would end up like so:
SELECT [customer_name], [customer_age], AVG([customer_age]) AS 'Average Customer Age' FROM [tb_customers] GROUP BY [customer_age]
How would I go about doing this? Is it even possible, as it seems like a very complex parsing task since the amount of fields is never going to be the same. If it helps, I've got a variable which stores the amount of fields in it (not including aggregates).
You may use a regular expression, like replace all occurrences of
AS \[.*?\]
with empty text
or all occurrences of
AS \[.*?\],
with a comma ",".
The question mark "?" is important here as it turns off greedy matching.
Related
I have multiple random numbers in table column ID like
8v12027
8v12025
8v12024
8v12029.
8v12023
8v12030
8v12020
O/p - 8v12020, From 8v12023 To 8v12025, 8v12027, From 8v12029 To 8v12030,
I assume you'are waiting for an sql solution so :
You have to use Lead or Lag KeyWord and concat it.
SELECT CONCAT('From ',Id,'To :', LEAD(p.Id) OVER (ORDER BY p.Id),'s') FROM YourTable p
There is a really good explanation about thoses keyword in the sqlauthority web site.
https://blog.sqlauthority.com/2013/09/22/sql-server-how-to-access-the-previous-row-and-next-row-value-in-select-statement/
But If you were waiting for a pure C# solution, you can retreive the data set in an Array, after order it by Id and concat and with a for loop concat current value with previous (or next) one.
Or with a Linq use Aggregate
yourArray.Aggregate((a,b)=> String.Concat("From ",a," To ",b,";")).Split(';')
I've got a DataTable in which there could be values in a column which looks like x:1 x:2 a:1 a:2 etc... but they could also look like x* or a*.
In my code I'm getting a full value to search for (for example x:1), but the row itself can contain a value like x* in that column.
can i somehow use the Select method to search for the row?
for now it looks something like this:
strSelect = string.Format("[{0}]='{1}'", colName, ValueToSearch);
rows = tempTable.Select(strSelect);
but of course that like that the only rows I'll get are those that look EXACTLY like the one in the table. meaning that when searching for x:1, i won't get the row with x*
The code strSelect = string.Format("[{0}]='{1}'", colName, ValueToSearch); will select the same values. If you want search for subset you must use LIKE operator:
strSelect = string.Format("[{0}] LIKE '{1}'", colName, ValueToSearch.Replace("*", "%");
I'm assuming for the moment that your database includes 4 rows, with the following values in a given column that you're wanting to query against:
x:1
x:2
x*
a:1
a:2
a*
You state that you're being handed a value such as 'x:1' which you need to use in your query, but you're implying that the query should end up return the first three records - those with values of 'x:1', 'x:2', and 'x*'. In other words, although you're being handed 'x:1', you're actually want to search for any records that have a value that begins with 'x'.
If that's the scenario, you're probably best off modifying the value in your C# code before issuing the query. If your search value is genuinely of the form 'x:1', you could just chop off the last two characters before handing it to the SQL query:
string searchValue = "x:1"; // this presumably actually comes from user input
searchValue = searchValue.Substring(0, searchValue.Length - 2);
// Now searchValue is just "x", so go ahead and create your SQL query using the 'LIKE' operator
I have the feeling this is just a simplification of your actual data though, which makes it hard to be precise & also makes it harder to provide an example that includes error-checking.
For a slightly more complex example, perhaps the search-value your user gives you can either be a string of letters, or a string of letters followed by a colon followed by more letters. In that case, you need to check whether the string you've been given contains a colon, and if it does you need to chop off the colon and anything following it:
string searchValue = "abc:def";
if (searchValue.Contains(":"))
searchValue = searchValue.Substring(0, searchValue.IndexOf(":"));
// Having stripped off ":def", you're left with "abc"
Now you can go ahead and issue a query, using the LIKE operator, as TcKs already showed in his answer. For example you could modify the query code you already have as follows:
strSelect = string.Format("[{0}] LIKE '{1}'", colName, ValueToSearch);
rows = tempTable.Select(strSelect);
By using the LIKE operator, you're now looking for any records that have a value which starts with "abc".
Im using the code from this MSDN page to create a user defined aggregate to concatenate strings with group by's in SQL server. One of my requirements is that the order of the concatenated values are the same as in the query. For example:
Value Group
1 1
2 1
3 2
4 2
Using query
SELECT
dbo.Concat(tbl.Value) As Concat,
tbl.Group
FROM
(SELECT TOP 1000
tblTest.*
FROM
tblTest
ORDER BY
tblTest.Value) As tbl
GROUP BY
tbl.Group
Would result in:
Concat Group
"1,2" 1
"3,4" 2
The result seems to always come out correct and as expected, but than I came across this page that states that the order is not guaranteed and that attribute SqlUserDefinedAggregateAttribute.IsInvariantToOrder is only reserved for future use.
So my question is: Is it correct to assume that the concatenated values in the string can end up in any order? If that is the case then why does the example code on the MSDN page use the IsInvariantToOrder attribute?
I suspect a big problem here is your statement "the same as in the query" - however, your query never defines (and cannot define) an order by the things being aggregated (you can of course order the groups, by having a ORDER BY after the GROUP BY). Beyond that, I can only say that it is based purely on a set (rather than an ordered sequence), and that technically the order is indeed undefined.
While the accepted answer is correct, I wanted to share a workaround that others may find useful. Warning: it involves not using a user-defined aggregate at all :)
The link below describes an elegant way to build a concatenated, delimited list using only a SELECT statement and a varchar variable. The upside (for this thread) is that you can specify the order in which the rows are processed. The downside is that you can't easily concatenate across many different subsets of rows without painful iteration.
Not perfect, but for my use case was a good workaround.
http://blog.sqlauthority.com/2008/06/04/sql-server-create-a-comma-delimited-list-using-select-clause-from-table-column/
I need to implement a method that takes an address split up into individual parts and returns any matching items from an address table. If no matches are found, I want to be able to return a value indicating where it failed. Each input param has a corresponding field in the table.
The signature would look something like this:
List<Address> MatchAddress(string zipCode, string streetName, string houseNumber, string houseLetter, string floor, string appartmentNo, out int mismatchPosition)
{
// return matching addresses
// if none found, return the position where it stopped matching
// zipCode is position 0, appartmentNo is position 5
//
// an empty param value indicates "don't check"
}
I know I can construct the method such that I start with all the parameters, execute the query and then remove param by param (from the right side) until either a match is found or I run out of parameters, but can I construct a query that is more effective than that, i.e minimizing the number of calls to the db, maybe even as a single call?
I think you could get a solution in one query using case statements and some nested queries, but before I go there, I'm not sure I follow what you're trying to accomplish. Assume the following sample data set:
ID House_Number Street_Name Zip_Code
=============================================
1 3012 Elm 10010
2 412 9th 10010
3 412 Main 90210
4 710 Main 60606
Also assume the following method call, passing in a zip, name and house number:
MatchAddress('10010', 'Main', '710')
What would you want to get back in this case? Your signature has a single out variable for the mismatch position, but this example would partially match all four of the records involved on at least one element. What would you want to return for record 1, for instance, where there's a match on zip code, but not on the house_number or street_name attributes?
======================================
OK, saw your comment. Here's a query that I think is on the right path for you. The WHERE clause is a series of ORs that return any record that matches on at least one of the criteria. Then the case statement looks to see where they're not equal to the passed in value. Obviously the least specific match is subjective, but you could re-order the case statement to put the desired match criteria in the order you want. I had this working on a MySQL db here.
SELECT address.*, CASE
WHEN zip_code <> '10010' THEN 'No match on Zip'
WHEN street_name <> 'Elm' THEN 'No match on Street Name'
WHEN house_number <> '29' THEN 'No match on House Number'
ELSE 'Match on all elements'
END AS zip_match
from address
where zip_code = '10010'
OR street_name = 'Elm'
OR house_number = '29'
I've made it return some pretty wordy statements for clarity here, but you could obviously have it return a number code or whatever makes sense for you. This also doesn't take into account issues like case sensitivity. As it's written, it's looking for precise case matching too. Depending on your db environment or how you're data is stored, you may need to address that if that's not what you want.
What's the best way to convert search terms entered by a user, into a query that can be used in a where clause for full-text searching to query a table and get back relevant results? For example, the following query entered by the user:
+"e-mail" +attachment -"word document" -"e-learning"
Should translate into something like:
SELECT * FROM MyTable WHERE (CONTAINS(*, '"e-mail"')) AND (CONTAINS(*, '"attachment"')) AND (NOT CONTAINS(*, '"word document"')) AND (NOT CONTAINS(*, '"e-learning"'))
I'm using a query parser class at the moment, which parses the query entered by users into tokens using a regular expression, and then constructs the where clause from the tokens.
However, given that this is probably a common requirement by a lot of systems using full-text search, I'm curious as to how other developers have approached this problem, and whether there's a better way of doing things.
How to implement the accepted answer using .Net / C# / Entity Framework...
Install Irony using nuget.
Add the sample class from:
http://irony.codeplex.com/SourceControl/latest#Irony.Samples/FullTextSearchQueryConverter/SearchGrammar.cs
Write code like this to convert the user-entered string to a query.
var grammar = new Irony.Samples.FullTextSearch.SearchGrammar();
var parser = new Irony.Parsing.Parser(grammar);
var parseTree = parser.Parse(userEnteredSearchString);
string query = Irony.Samples.FullTextSearch.SearchGrammar.ConvertQuery(parseTree.Root);
Perhaps write a stored procedure like this:
create procedure [dbo].[SearchLivingFish]
#Query nvarchar(2000)
as
select *
from Fish
inner join containstable(Fish, *, #Query, 100) as ft
on ft.[Key] = FishId
where IsLiving = 1
order by rank desc
Run the query.
var fishes = db.SearchLivingFish(query);
This may not be exactly what you are looking for but it may offer you some further ideas.
http://www.sqlservercentral.com/articles/Full-Text+Search+(2008)/64248/
In addition to #franzo's answer above you probably also want to change the default stop word behaviour in SQL. Otherwise queries containing single digit numbers (or other stop words) will not return any results.
Either disable stop words, create your own stop word list and/or set noise words to be transformed as explained in SQL 2008: Turn off Stop Words for Full Text Search Query
To view the system list of (English) sql stop words, run:
select * from sys.fulltext_system_stopwords where language_id = 1033
I realize it's a bit of a side-step from your original question, but have you considered moving away from SQL fulltext indexes and using something like Lucene/Solr instead?
The easiest way to do this is to use dynamic SQL (I know, insert security issues here) and break the phrase into a correctly formatted string.
You can use a function to break the phrase into a table variable that you can use to create the new string.
A combination of GoldParser and Calitha should sort you out here.
This article: http://www.15seconds.com/issue/070719.htm has a googleToSql class as well, which does some of the translation for you.