SQL - querying via a textbox which could take different values - asp.net

Developing a website and just trying to get back into the swing of (clever) SQL queries etc, my mind had totally gone tonight!
There is a website http://www.ufindus.com/ which has a textbox allowing you to enter either a place name or a postcode/zipcode. I am trying to do something similiar but I am rubbish at SQL - so how do you construct an SQL statement that could potentially look at 2 columns (i.e. place and postcode) because you can't query both fields for the same value e.g
place = 'YORK' AND postcode = 'YORK'
or
place = 'YO21 5EA' AND postcode = 'YO21 5EA'
so do you have to put some logic in to be intelligent enough to detect whether it looks like a place name or a postcode - that just seems too complicated to me!! Any help would be much appreciated.

You could use an "OR" to get the job done. For example,
place = 'YORK' or postcode = 'YORK'
You might also do better using the LIKE statement, as in
WHERE place LIKE 'YORK%' or postcode LIKE 'YORK%'
(this assumes both place and postcode are character-based columns)

why not use OR instead of AND?
place = #textboxvalue OR post = #textboxvalue

What's wrong with attempting to match on the place and postcode? If I put in 'York' and (somewhere) that happens to be a valid postcode, I should get that result. As for preventing the same thing being entered twice, well, you can handle that on the validation prior to doing the database call.
Ah. Guess I was a bit slow on the up-take. Yes... what the others suggested is right, 'OR' is what you were looking for. I misinterpreted.

Ok, first I'm assuming that you have a table with a mapping of postcodes to placenames.
Let's call this table 'postcode' with columns 'postcode' and 'postplace'. Both of these are of a char-type.
Then.. whatever you do, make sure the input from the user is not part of dynamic sql. Make sure it is a parameter. Otherwise, you are inviting SQL injection attacks that can really ruin your day. This is important.
Our user input is in #textboxstring.
Given this, you can get the postcode and postplace like this:
select #textboxstring = RTRIM(#textboxstring) + '%';
select postcode, postplace
from postcode
where postcode like #textboxstring or postplace like #textboxstring;
Note that I'm modifying #textboxstring to get wildcard match with like without having to use dynamic sql.
If the postcode was integer, you would need to convert the input to int before executing the sql. So with a #textboxint as well, you could do this:
select #textboxstring = RTRIM(#textboxstring) + '%';
select postcode, postplace
from postcode
where postcode = #textboxint or postplace like #textboxstring;
Oh, and you need to handle that your search can have multiple results. You probably only want the first row.

Related

How to Join a data source using a `Like` `LEFT`

I first created a SQL query and now I'm converting it into an AOT query in AX 2012 R3. I have everything else working except for one thing. I have a join in my SQL that is: JOIN dbo.INVENTJOURNALTABLE AS INV3 ON INV.INVENTBATCHID LIKE left(INV3.DESCRIPTION,17). In SQL this works and returns the data that I need.
I don't know how to create this join in the AOT query though.
That INV3.DESCRIPTION is a string that contains the InventBatchID.
Is there a way to do this kind of join?
In order to accomplish what you want with a Query object, it's difficult/unusual, and AX isn't designed to do this in a straight forward way and requires a very creative solution.
I would generally push towards writing a pure x++ query as I don't believe LEFT and LIKE can be natively combined, especially in a query. You can use * in the value for "like" as an option.
You may be able to accomplish using expressions in query ranges somehow.
If you must have a query, a way I can think is by combining a View, Computed Column, and a Query...and I can't guarantee it will work, but I can give you enough to have something to test with. Some of the information in this answer is pretty concentrated so look closely at everything to understand it.
Create a View, add the below computed column, then add it to a Query as pictured below. For the computed column, you need to add a new String field to the view and set the ViewMethod property to the name of the method. See here for more info about adding Computed Columns.
The Computed Column accomplishes the left(..., 17) part and you can browse the view to confirm.
The last part is trying to join either by a relation (pictured below, but it does not accomplish the like) or setting the Value property using an expression by following the link above. You may need to create a custom expression in \Classes\SysQueryRangeUtil. You have some experimenting to do to see if it works.
private static server str compColDescLeft()
{
#define.ViewName(InventJournalTableView)
#define.DataSourceName("InventJournalTable_1")
#define.FieldDescription("Description")
#define.LeftCount("17")
str sReturn;
str sLeftDesc;
DictView dictView;
dictView = new DictView(tableNum(#ViewName));
sLeftDesc = dictView.computedColumnString(#DataSourceName, #FieldDescription, FieldNameGenerationMode::FieldList, true);
sReturn = "left(" + sLeftDesc + ", " + #LeftCount + ")";
return sReturn;
}

Access database UPDATE table with subquery

I never should've expected that knowing mySQL I'd be safe using Access.
I have two tables: users and scores
users table contains: id(auto increment primary key), username, password, etc..
scoers table contains: id(number - foreign key to users.id), highScore
I've previously asked help for INSERT command, which now works as it should. Now I've got issues with a similar UPDATE command.
The non-working command looks like this:
string updateCommand = #"UPDATE scores
SET
id = (SELECT id FROM users WHERE username = #username),
highScore = #score
WHERE highScore = (SELECT MIN(highScore) FROM scores);";
which throws a: Operation must use an updateable query.
To rationalize what I'm trying to accomplish here: I'm INSERT-ing high scores until I reach 10 scores in the table, afterwards instead of adding any new scores and filling up the database needlessly I decided It'd be more sensible to just "overwrite" the currently lowest score using UPDATE.
I am supplied a username and the high score and since the scores table contains only id I need to reach the id of the current user so that's what the first subquery is doing, the second subquery in the WHERE clause is to specify which score to replace (though there is possibly a bug here if there are multiple people with the lowest score, any ideas how to fix that?)
I've also tried using OUTER RIGHT JOIN like this:
string updateCommand = #"UPDATE scores
OUTER RIGHT JOIN users ON scores.id = users.id
SET
scores.id = users.id,
scores.highScore = #score
WHERE (highScore = (SELECT MIN(highScore) FROM scores)) AND (username = #username);";
With no luck(I get a generic Syntax error in UPDATE statement.).
Browsing the net I've found that I possibly "can't" use subqueries in UPDATE statements but I seem to find conflicting opinions on the matter.
I've also tried using the DLookup function in place of subqueries like:
#"...
id = DLookup(""id"", ""users"", ""username = #username""),
...
WHERE highScore = DLookup(""MIN(highScore)"", ""scores"");";
elipses represent extraneous code which is identical to the code above.
Also as a last resort I've tried dividing into multiple queries however userId query which looks like this:
string userIdQuery = "SELECT id FROM users WHERE username = #username"
seems to return a null judging by the NullReferenceException i recieve (Object reference not set to an instance of an object.) when trying to use the variable userId after I've done this:
int userId = 0;
userId = (Int32)command.ExecuteScalar();
I'm supposed to get an integer however I get a null I think. The almost identical query for getting the minimum highscore works flawlessly and the int variable is filled with the correct value so I'm assuming that hte problem is in the query itself somehow. I've tried adding single quotes around the #username parameter assuming that it might not be recognizing the string but it seems that's not it.
Phew.. took me a while to write this. Anyone got any ideas on how to make this all work? If you need more info let me know.
So after some messing around I've found out the causes of my troubles. The bad side is that I increased the amount of code so that I'd avoid subqueries as much as possible since, at least from my experience, Access doesn't really like the use of subqueries in UPDATE or INSERT commands.
What I did first is split the command into 3 separate ones:
"SELECT id FROM users WHERE username = ?;" - To get the id of the user whose score
I'm putting in the database.
#"SELECT scores.id, scores.highScore, scores.dateTime FROM scores WHERE (((scores.highScore)=DMin(""highScore"",""scores"")));" - which gets the id, high score
and time when the entry was... well entered, of the lowest score currently in the high scores list. Thanks to a suggestion from HansUp I used DMin function instead of a subquery with MIN to avoid the Must use an updateable query error. The extraneous parentheses are due to Access since this command was generated by the Access query designer and I'm too afraid to change anything lest I break it.
#"UPDATE scores SET scores.id = ?, scores.highScore = ?, scores.[dateTime] = Now() WHERE (((scores.id)=?) AND ((scores.highScore)=?) AND ((scores.dateTime)=?));" - The update command itself, not much to say here except that it takes the previously extracted data and uses it as values for the command.
One thing I noticed is that even if I got the command working the .ExecuteNonQuery() would always return 0 rows affected. After poking around I found out that named parameters for commands in ASP.NET / C# don't always work and that instead ? should be used instead. It's kind of inconvenient but I can't complain too much.

DataColumn.Expression RowFilter on Dataview

Maybe this is stupid question or maybe I have designed my code completely wrong but anyhow, here is my question...
I have a "dynamic" sql-query where its impossible to take all the parameters i need for making the query parameterized, therefore i get my data and put it in a dataview and after that i search for the rows I want to show in the dataview.
One of the columns are a column named id. Id is primary key and auto_increment in the table and therefore it's an int.
Now to my question, i want to present all my matching id with the number the user put in my textbox. Let us say my id consist of 5 numbers and the user put the 4 first, then in the perfect world i would have 10 matches (12340-12349 as an example). Doing this on a string is very easy using RowFilter and the operator LIKE combined with a wildcard. But how can i do something similar on integers? Do i have to convert it to strings and wont that ruin the rowfilter expression?
Not a live or death-situation... im more curious if the ice im walking is very thin... :)
Rowfilter expression supports CONVERT function, so technically you can convert your integer ID to string to do the LIKE command:
MyDataView.RowFilter = "Convert(ID, 'System.String') LIKE '1234*'";
But do try to offload the filtering to backend. It's unlikely that you have unlimited number of parameters and SQL is very flexible in allowing you different combinations.

Database schema advice for storing form fields and field values

I've been tasked with creating an application that allows users the ability to enter data into a web form that will be saved and then eventually used to populate pdf form fields.
I'm having trouble trying to think of a good way to store the field values in a database as the forms will be dynamic (based on pdf fields).
In the app itself I will pass data around in a hash table (fieldname, fieldvalue) but I don't know the best way to convert the hash to db values.
I'm using MS SQL server 2000 and asp.net webforms. Has anyone worked on something similar?
Have you considered using a document database here? This is just the sort of problem they solve alot better than traditional RDBMS solutions. Personally, I'm a big fan of RavenDb. Another pretty decent option is CouchDb. I'd avoid MongoDb as it really isn't a safe place for data in it's current implementation.
Even if you can't use a document database, you can make SQL pretend to be one by setting up your tables to have some metadata in traditional columns with a payload field that is serialized XML or json. This will let you search on metadata while staying out of EAV-land. EAV-land is a horrible place to be.
UPDATE
I'm not sure if a good guide exists, but the concept is pretty simple. The basic idea is to break out the parts you want to query on into "normal" columns in a table -- this lets you query in standard manners. When you find the record(s) you want, you can then grab the CLOB and deserialize it as appropriate. In your case you would have a table that looked something like:
SurveyAnswers
Id INT IDENTITY
FormId INT
SubmittedBy VARCHAR(255)
SubmittedAt DATETIME
FormData TEXT
A few protips:
a) use a text based serialization routine. Gives you a fighting chance to fix data errors and really helps debugging.
b) For SQL 2000, you might want to consider breaking the CLOB (TEXT field holding your payload data) into a separate table. Its been a long time since I used SQL 2000, but my recollection is using TEXT columns did bad things to tables.
The solution for what you're describing is called Entity Attribute Value (EAV) and this model can be a royal pain to deal with. So you should limit as much as possible your usage of this.
For example are there fields that are almost always in the forms (First Name, Last Name, Email etc) then you should put them in a table as fields.
The reason for this is because if you don't somebody sooner or later is going to realize that they have these names and emails and ask you to build this query
SELECT
Fname.value fname,
LName.Value lname,
email.Value email,
....
FROM
form f
INNER JOIN formFields fname
ON f.FormId = ff.FormID
and AttributeName = 'fname'
INNER JOIN formFields lname
ON f.FormId = ff.FormID
and AttributeName = 'lname'
INNER JOIN formFields email
ON f.FormId = ff.FormID
and AttributeName = 'email'
....
when you could have written this
SELECT
common.fname,
common.lname,
common.email,
....
FROM
form f
INNER JOIN common c
on f.FormId = c.FormId
Also get off of SQL 2000 as soon as you can because you're going to really miss the UNPIVOT clause
Its also probably not a bad idea to look at previous SO EAV questions to give you an idea of problems that people have encountered in the past
I'd suggest mirroring the same structure:
Form
-----
form_id
User
created
FormField
-------
formField_id
form_id
name
value

Getting out a single value from a sql database (asp.net)

I'm trying to get a single value from my table in a database. They are all given a unique id when stored. Here is the code I use to put the in:
With SqlDataSource1
.InsertParameters("page").DefaultValue = ViewState("TrueURL")
If My.User.IsAuthenticated Then
.InsertParameters("sender").DefaultValue = My.User.Name
Else
.InsertParameters("sender").DefaultValue = Request.UserHostAddress.ToString
End If
.InsertParameters("details").DefaultValue = PrepareText(TextBox2.Text)
.InsertParameters("date").DefaultValue = Now()
.Insert()
End With
That was so you could get an idea of what I was looking for, I'm not looking for sql statements, I don't know how to use them.
Just keep in mind, this is all vb.net/asp.net.
Thanks!
--EDIT--
I think I found something useful but I can't find how to use it. The Select function. It returns something and accepts parameters like the insert one I mentioned above... any ideas?
You cant extract it without first running an SQL query to extract it.
If you've got this far, read a little further in the MS help pages about how to run the select querys, this is where you would put your SQL statements.

Resources