Source file: Canabalt.swf
Source file: barbarian-onslaught--3991.swf
Source file: Canabalt wide mega.swf
Source file: Canabalt wide normal.swf
Source file: Canabalt.swf
Source file: barbarian-onslaught--3991.swf
Source file: Canabalt wide mega.swf
Source file: Canabalt wide normal.swf
ASP.Net includes quite a plethora of properties to retrieve path information about the current request, control and application. There's a ton of information available about paths on the Request object, some of it appearing to overlap and some of it buried several levels down, and it can be confusing to find just the right path that you are looking for.
To keep things straight I thought it a good idea to summarize the path options along with descriptions and example paths. I wrote a post about this a long time ago in 2004 and I find myself frequently going back to that page to quickly figure out which path I’m looking for in processing the current URL. Apparently a lot of people must be doing the same, because the original post is the second most visited even to this date on this blog to the tune of nearly 500 hits per day. So, I decided to update and expand a bit on the original post with a little more information and clarification based on the original comments.
Here's a list of the Path related properties on the Request object (and the Page object). Assume a path like http://www.west-wind.com/webstore/admin/paths.aspx for the paths below where webstore is the name of the virtual.
| Request Property | Description and Value |
| ApplicationPath | Returns the web root-relative logical path to the virtual root of this app. /webstore/ |
| PhysicalApplicationPath | Returns local file system path of the virtual root for this app. c:\inetpub\wwwroot\webstore |
| PhysicalPath | Returns the local file system path to the current script or path. c:\inetpub\wwwroot\webstore\admin\paths.aspx |
| CurrentExecutionFilePath FilePath Path | All of these return the full root relative logical path to the script page including path and scriptname. /webstore/admin/paths.aspx |
| AppRelativeCurrentExecutionFilePath | Returns an ASP.NET root relative virtual path to the script or path ~/admin/paths.aspx |
| PathInfo | Returns any extra path following the script name. If no extra path is provided returns the root-relative path (returns text in red below). string.Empty if no PathInfo is available. /webstore/admin/paths.aspx/ExtraPathInfo |
| RawUrl | Returns the full root relative relative URL including querystring and extra path as a string. /webstore/admin/paths.aspx?sku=wwhelp40 |
| Url | Returns a fully qualified URL including querystring and extra path. Note this is a Uri instance rather than string. http://www.west-wind.com/webstore/admin/paths.aspx?sku=wwhelp40 |
| UrlReferrer | The fully qualified URL of the page that sent the request. This is also a Uri instance and this value is null if the page was directly accessed by typing into the address bar or using an HttpClient. Based Referrer client Http header. http://www.west-wind.com/webstore/default.aspx?Info |
| Control.TemplateSourceDirectory | Returns the logical path to the folder of the page, master or user control on which it is called. This is useful if you need to know the path only to a Page or control from within the control. For non-file controls this returns the Page path. /webstore/admin/ |
As you can see there’s a ton of information available there for each of the three common path formats:
You should be able to get any necessary format from ASP.NET from just about any path or script using these mechanisms.
ASP.NET supports root-relative virtual path syntax in most of its URL properties in Web Forms. So you can easily specify a root relative path in a control rather than a location relative path:
<asp:Image runat="server" ID="imgHelp" ImageUrl="~/images/help.gif" />
ASP.NET internally resolves this URL by using ResolveUrl("~/images/help.gif") to arrive at the root-relative URL of /webstore/images/help.gif which uses the Request.ApplicationPath as the basepath to replace the ~. By convention any custom Web controls also should use ResolveUrl() on URL properties to provide the same functionality.
In your own code you can use Page.ResolveUrl() or Control.ResolveUrl() to accomplish the same thing:
string imgPath = this.ResolveUrl("~/images/help.gif"); imgHelp.ImageUrl = imgPath;
Unfortunately ResolveUrl() is limited to WebForm pages, so if you’re in an HttpHandler or Module it’s not available.
ASP.NET Mvc also has it’s own more generic version of ResolveUrl in Url.Decode:
<script src="<%= Url.Content("~/scripts/new.js") %>" type="text/javascript"></script>
which is part of the UrlHelper class. In ASP.NET MVC the above sort of syntax is actually even more crucial than in WebForms due to the fact that views are not referencing specific pages but rather are often path based which can lead to various variations on how a particular view is referenced.
In a Module or Handler code you can also rely on the static VirtualPathUtility class:
string path = VirtualPathUtility.ToAbsolute("~/admin/paths.aspx");
VirtualPathUtility also many other quite useful methods for dealing with paths and converting between the various kinds of paths supported. One thing to watch out for is that ToAbsolute() will throw an exception if a query string is provided and doesn’t work on fully qualified URLs. I wrote about this topic with a custom solution that works fully qualified URLs and query strings here (check comments for some interesting discussions too).
If you need to map root relative or current folder relative URLs to physical URLs or you can use HttpContext.Current.Server.MapPath(). Inside of a Page you can do the following:
string physicalPath = Server.MapPath("~/scripts/ww.jquery.js"));
MapPath is pretty flexible and it understands both ASP.NET style virtual paths as well as plain relative paths, so the following also works.
string physicalPath = Server.MapPath("scripts/silverlight.js");
as well as dot relative syntax:
string physicalPath = Server.MapPath("../scripts/jquery.js");
Once you have the physical path you can perform standard System.IO Path and File operations on the file. Remember with physical paths and IO or copy operations you need to make sure you have permissions to access files and folders based on the Web server user account that is active (NETWORK SERVICE, ASPNET typically).
Between these settings you can get all the information you may need to figure out where you are at and to build new Url if necessary. If you need to build a URL completely from scratch you can get access to information about the server you are accessing:
| Server Variable | Function and Example |
| SERVER_NAME | The of the domain or IP Address wwww.west-wind.com or 127.0.0.1 |
| SERVER_PORT | The port that the request runs under. 80 |
| SERVER_PORT_SECURE | Determines whether https: was used. 0 or 1 |
| APPL_MD_PATH | ADSI DirectoryServices path to the virtual root directory. Note that LM typically doesn’t work for ADSI access so you should replace that with LOCALHOST or the machine’s NetBios name. /LM/W3SVC/1/ROOT/webstore |
If you still need more control over the current request URL or you need to create new URLs from an existing one, the current Request.Url Uri property offers a lot of control. Using the Uri class and UriBuilder makes it easy to retrieve parts of a URL and create new URLs based on existing URL. The UriBuilder class is the preferred way to create URLs – much preferable over creating URIs via string concatenation.
| Uri Property | Function |
| Scheme | The URL scheme or protocol prefix. http or https |
| Port | The port if specifically specified. |
| DnsSafeHost | The domain name or local host NetBios machine name www.west-wind.com or rasnote |
| LocalPath | The full path of the URL including script name and extra PathInfo. /webstore/admin/paths.aspx |
| Query | The query string if any ?id=1 |
The Uri class itself is great for retrieving Uri parts, but most of the properties are read only if you need to modify a URL in order to change it you can use the UriBuilder class to load up an existing URL and modify it to create a new one.
Here are a few common operations I’ve needed to do to get specific URLs:
Convert the Request URL to an SSL/HTTPS link
For example to take the current request URL and converted it to a secure URL can be done like this:
UriBuilder build = new UriBuilder(Request.Url); build.Scheme = "https"; build.Port = -1; // don't inject port Uri newUri = build.Uri; string newUrl = build.ToString();
Retrieve the fully qualified URL without a QueryString
AFAIK, there’s no native routine to retrieve the current request URL without the query string. It’s easy to do with UriBuilder however:
UriBuilder builder = newUriBuilder(Request.Url);
builder.Query = "";
stringlogicalPathWithoutQuery = builder.ToString();
Trovato qui.
SET NOCOUNT ON;
DECLARE @tblTmpViews TABLE (view_name varchar(100));
DECLARE @view_name nvarchar(100);
INSERT @tblTmpViews SELECT [name] FROM sysobjects WHERE xtype='V';
DELETE @tblTmpViews WHERE view_name IN ('tax_OrfaniSanguinettiVP_vl', 'tax_OrfaniSanguinettiFS_vl', 'tax_OrfaniSanguinettiVU_vl');
DELETE @tblTmpViews WHERE view_name LIKE '%ven_RicercaContratti_vl%';
DECLARE crsrViews CURSOR FOR SELECT view_name FROM @tblTmpViews ORDER BY view_name;
OPEN crsrViews;
FETCH NEXT FROM crsrViews INTO @view_name;
WHILE @@FETCH_STATUS = 0 BEGIN PRINT 'Refreshing view ' + @view_name + '...' EXEC sp_refreshview @view_name FETCH NEXT FROM crsrViews INTO @view_name END CLOSE crsrViews;
DEALLOCATE crsrViews;
PRINT '';
PRINT 'Refresh done successfully.';
SET NOCOUNT OFF;SET NOCOUNT ON; DECLARE @tblTmpViews TABLE (view_name varchar(100)); DECLARE @view_name nvarchar(100); INSERT @tblTmpViews SELECT [name] FROM sysobjects WHERE xtype='V'; DELETE @tblTmpViews WHERE view_name IN ('tax_OrfaniSanguinettiVP_vl', 'tax_OrfaniSanguinettiFS_vl', 'tax_OrfaniSanguinettiVU_vl'); DELETE @tblTmpViews WHERE view_name LIKE '%ven_RicercaContratti_vl%'; DECLARE crsrViews CURSOR FOR SELECT view_name FROM @tblTmpViews ORDER BY view_name; OPEN crsrViews; FETCH NEXT FROM crsrViews INTO @view_name; WHILE @@FETCH_STATUS = 0 BEGIN PRINT 'Refreshing view ' + @view_name + '...' EXEC sp_refreshview @view_name FETCH NEXT FROM crsrViews INTO @view_name END CLOSE crsrViews; DEALLOCATE crsrViews; PRINT ''; PRINT 'Refresh done successfully.'; SET NOCOUNT OFF;
' Invoke a method via reflection and return its result - return null if method
' doesn't exist or throws
' Note: requires Imports System.Reflection
'
' Example:
' Function GetCompleteName(ByVal firstName As String,
' ByVal lastName As String)
' Return lastName & ", " & firstName
' End Function
' ...
' MessageBox.Show(InvokeMethod(Me, "GetCompleteName", False, "Marco",
' "Bellinaso"))
Function InvokeMethod(ByVal obj As Object, ByVal methodName As String, _
ByVal throwIfError As Boolean, ByVal ParamArray args() As Object) As Object
Try
Return obj.GetType().InvokeMember(methodName, BindingFlags.Instance Or _
BindingFlags.InvokeMethod Or BindingFlags.Public, Nothing, obj, _
args)
Catch ex As Exception
If throwIfError Then Throw ex
End Try
' if method doesn't exists or throws
Return Nothing
End Function
Trovato qui.
Here's an easy way to check if a temp table exists, before trying to create it (ie. for reusable scripts):
IF object_id('tempdb..#MyTempTable') IS NOT NULL
BEGIN
DROP TABLE #MyTempTable
END
CREATE TABLE #MyTempTable
(
ID int IDENTITY(1,1),
SomeValue varchar(100)
)
GO
That way, if you have to change databases in the query window, you don't have to drop the tables before you run it again.
Trovato qui.
This article was previously published under Q327084
Microsoft Visual Basic version 6.0 cannot use the .NET method with the ParamArray parameter in Microsoft Visual Studio .NET. A compile-time error is generated by Visual Basic version 6.0 when it tries to consume the .NET method that has the ByRef ParamArray parameter or theByRef Structure parameter.
When the .NET method has the ByRef ParamArray parameter, you receive the following error message:
Compile error:
Function or interface marked as restricted, or the function uses an Automation type not supported in Visual Basic
When the .NET method has the ByRef Structure parameter, you receive the following error message:
Compile error:
User-defined type may not be passed ByVal
This problem occurs because Visual Basic version 6.0 does not let the ParamArray parameter and the Structure parameter be passed to the BYVAL value. This problem occurs with a .NET property because a .NET property does not let property parameters be defined by the BYREF value.
To work around this problem, you can define the ParamArray parameter and the Structureparameter as BYREF. For a property, you can add a method with the ByRef parameter that assigns the property to the private member.
Microsoft has confirmed that this is a bug in the Microsoft products that are listed in the "Applies to" section.
<ComClass()> Public Class Class1
Dim _res(4) As Byte
Public Property res() As Byte()
Get
Return _res
End Get
Set(ByVal Value As Byte())
_res = Value
End Set
End Property
Public Structure s1
Public i As Integer
dim j as integer
End Structure
Dim _mes As s1
Public Property mes() As s1
Get
Return _mes
End Get
Set(ByVal Value As s1)
_mes = Value
End Set
End Property
Public Sub setArrayProp(ByRef Value As Byte())
_res = Value
End Sub
Public Sub setStructProp(ByRef Value As s1)
_mes = Value
End Sub
End Class
Private Sub Command1_Click()
Dim cls As New Class1
Dim arr() As Byte
Dim s As s1
arr = cls.res
'cls.res = arr
s = cls.mes
'cls.mes = s
Call cls.setArrayProp(arr)
Call cls.setStructProp(s)
End Sub
Trovato qui.
Gli HTTP Handler sono dei meccanismi di ASP.NET che si occupano di elaborare le risposte a specifiche richieste HTTP e fornirle, secondo la forma più consona, al richiedente.
Questo Generic Handler restituisce un'immagine a caso tra quelle presenti in una directory di un sito web.
Si tratta di un file con estensione .ashx che non ha bisogno di essere compilato, né di essere registrato nel file web.config. Basterà solamente far riferimento ad esso come URL di un'immagine, indipendentemente da dove questo sia utilizzato.
Ad esempio, supponendo di chiamare il nostro file ImmagineCasuale.ashx e di metterlo nella root del sito web www.miosito.it, potremmo utilizzarlo per visualizzare un'immagine casuale In diversi modi.
<img src="/ImmagineCasuale.ashx" alt="immagine casuale"/>
div.intestazione {
background-image: url (/ImmagineOraria.ashx);
}
<asp:Image ID="ImmagineCasuale" runat="server" ImageUrl="~/ImmagineCasuale.ashx"
AlternateText="Immagine Casuale" />
E' possibile, inoltre, chiedere l'immagine casuale da un altro sito, sarà sufficiente indicare l'URL completo: http://www.miosito.it/ImmagineCasuale.ashx
Ecco il codice:
<%@ WebHandler Language="VB" Class="ImmagineCasuale" %>
Imports System
Imports System.Web
Public Class ImmagineCasuale : Implements IHttpHandler
' qui indico la directory in cui ci sono le immagini
Const DIRIMMAGINI = "immagini/fotocasuali"
' implementazione del metodo
Public Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
' metto i nomi dei file .jpg in un array
' (potrei farlo anche per altre estensioni)
Dim nomiFileImmagine() As String = _
System.IO.Directory.GetFiles(context.Server.MapPath(DIRIMMAGINI), "*.jpg")
' se ci sono immagini ne estraggo una a caso e la invio come HttpResponse
If nomiFileImmagine.Length > 0 Then
Dim n As Integer
Dim vMax As Integer
vMax = nomiFileImmagine.GetUpperBound(0)
Randomize()
n = CInt(Int((vMax + 1) * Rnd()))
Dim response As Web.HttpResponse = context.Response
response.ContentType = "image/jpeg"
response.Cache.SetCacheability(HttpCacheability.Public)
response.BufferOutput = False
response.WriteFile(nomiFileImmagine(n))
response.End()
End If
End Sub
' poiché si tratta di sola lettura imposto IsReusable a True
Public ReadOnly Property IsReusable() As Boolean _
Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
End Class
Un esempio di possibile utilizzo potrebbe essere un banner pubblicitario di una pagina web, in cui ad ogni richiesta lo sponsor varia casualmente.
Possibili varianti: cambiare la directory o scegliere l'immagine a seconda dei parametri passati attraverso una QueryString, oppure scegliere l'immagine a seconda dell'ora.
In quest'ultimo caso il codice potrebbe essere:
<%@ WebHandler Language="VB" Class="ImmagineOraria" %>
Imports System
Imports System.Web
Public Class ImmagineOraria : Implements IHttpHandler
Const DIRIMMAGINI = "immagini/orarie"
Public Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
Dim nomiFileImmagine() As String = _
{"mattino", "giorno", "pomeriggio", "sera", "notte"}
Dim n, ora As Integer
ora = DateTime.Now.Hour
Select Case ora
Case 22 To 24, 0 To 6
n = 4
Case 7 To 9
n = 0
Case 17 To 19
n = 2
Case 20 To 21
n = 3
Case Else
n = 1
End Select
Dim response As Web.HttpResponse = context.Response
response.ContentType = "image/jpeg"
response.Cache.SetCacheability(HttpCacheability.Public)
response.BufferOutput = False
response.WriteFile(context.Server.MapPath(DIRIMMAGINI & _
nomiFileImmagine(n) & ".jpg"))
response.End()
End Sub
Public ReadOnly Property IsReusable() As Boolean _
Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
End Class
Trovato qui.
<table> with five rows in total - one row for each of the three TextBox controls, one for the Login Button control, and one that contains displays a message upon log in failure. In addition to the three TextBox controls the page contains three RequiredFieldValidator controls to ensure that the user supplies values for each of the inputs. (For more information on ASP.NET's validation controls, see Form Validation with ASP.NET - It Doesn't Get Any Easier!.) <table> within the Login control's declarative syntax. I then deleted the Login control tags (<asp:Login> and </asp:Login>), leaving the markup. Finally, I added a new table row for the "Log In As User Name" UI and removed the "Remember Me" CheckBox row (because I do not want to allow an Admin user to log on as another user and have that remembered across browser restarts). <table> and the first row that contains the TextBox for the Admin user's username.<table border="0" cellpadding="2" cellspacing="0"> <tr class="AdminUserPrompt"> <td align="right"> <asp:Label ID="AdminUserNameLabel" runat="server" AssociatedControlID="AdminUserName">An <b>Admin</b> User Name:</asp:Label> </td> <td> <asp:TextBox ID="AdminUserName" runat="server"></asp:TextBox> <asp:RequiredFieldValidator ID="AdminUserNameRequired" runat="server" ControlToValidate="AdminUserName" ErrorMessage="The Admin User Name is required." ToolTip="The Admin User Name is required." ValidationGroup="LogInAs">*</asp:RequiredFieldValidator> </td> </tr> </table>Logging in the User After entering the Admin user's credentials, the name of the user to login as, and clicking the Login button, a postback occurs. In the Login Button's
Click event handler we need to: validate the Admin user's credentials; assure that the Admin user is actually in the Admin role; and verify that the username to log in as exists in the system. If all that checks out, we need to create a forms authentication ticket for the user. The following code shows the Click event handler that handles this logic: Protected Sub LoginButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LoginButton.Click
'Make sure that the Admin username & password are valid
If Membership.ValidateUser(AdminUserName.Text, AdminPassword.Text) Then
'Yes, this username/password is good, but is this user in the Admin role?
If Roles.IsUserInRole(AdminUserName.Text, "Admin") Then
'Great, this user is in the Admin role! Now, is the username to login as a valid user?
Dim LogInAsUser As MembershipUser = Membership.GetUser(LogInAsUserName.Text)
If LogInAsUser IsNot Nothing Then
'Yes, this user is valid! Great, let's log in as that user!
FormsAuthentication.RedirectFromLoginPage(LogInAsUserName.Text, False)
Else
'The Admin username/password is kosher, but the user to log in as was not found
FailureText.Text = String.Format("The user {0} does not exist in the Membership database.", AdminUserName.Text)
End If
Else
'The user credentials for Admin user are valid, but the user is not an Admin
FailureText.Text = "Only Admins can log into the site as another user. To login as yourself, please visit the standard Login page."
End If
Else
'The Admin username/password are invalid
FailureText.Text = "Your login attempt was not successful. Please try again."
End If
End SubThe code starts by calling the Membership.Validate(username, password) method, which returns True if the supplied credentials are valid, False otherwise. The username and password entered into the Admin username and password TextBox controls are passed in as the username and password parameters to this method. If this returns True then we next use the Roles API to ensure that the user is a member of the Admin role. If it returns False, the message "Your login attempt was not successful. Please try again" is displayed. Roles.IsUserInRole(username, role) returns a Boolean value indiciating whether username exists in the role role. If the user is indeed an Admin then the last step is to check whether the username of the user to log in as is indeed valid. If the user is not in the Admin role then the message "Only Admins can log into the site as another user. To login as yourself, please visit the standard Login page" is displayed. Membership.GetUser(username) , which returns a MembershipUser object if the username is valid. If the user is not found then it returns Nothing (null in C#). If the user account exist we log in as that user by creating a forms authentication ticket with that user's identity. The FormsAuthentication class contains methods for creating and managing forms authentication tickets. The RedirectFromLoginPage(username, persistentCookie) method creates a forms authentication ticket for user username, adds it to the visitor's Cookies collection, and then redirects the user to the appropriate page (the URL specified in the ReturnUrl querystring value, if present; Default.aspx otherwise). FormsAuthentication.RedirectFromLoginPage method automatically creates a forms authentication ticket and adds it to the visitor'sCookies collection. The FormsAuthentication class also contains a GetAuthCookie(username, persistCookie) method that creates the forms authentication ticket and returns it, but does not add it to the UserId value, making it clear that Scott placed the order on Sam's behalf.) FormsAuthentication.RedirectFromLoginPage with the following code: ' Create the cookie that contains the forms authentication ticket
Dim authCookie As HttpCookie = FormsAuthentication.GetAuthCookie(LogInAsUserName.Text, False)
' Get the FormsAuthenticationTicket out of the encrypted cookie
Dim ticket As FormsAuthenticationTicket = FormsAuthentication.Decrypt(authCookie.Value)
' Create a new FormsAuthenticationTicket that includes our custom User Data
Dim newTicket As FormsAuthenticationTicket = New FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, AdminUserName.Text)
' Update the authCookie's Value to use the encrypted version of newTicket
authCookie.Value = FormsAuthentication.Encrypt(newTicket)
' Manually add the authCookie to the Cookies collection
Response.Cookies.Add(authCookie)
'Return the user to the homepage
Response.Redirect("~/Default.aspx")
The above code starts by calling FormsAuthentication.GetAuthCookie to create the forms authentication cookie for the user who the Admin user wants to log in as. This cookie is actually encrypted so as to protect its contents as the cookie travels over the wire from the visitor's browser to the web server. Therefore, we need to descrypt it before we can work with it; this is accomplished by using theFormsAuthentication.Decrypt method. Next, we create a new forms authentication ticket, this time indicating that the Admin user's username should be included in the ticket's contents. This new authentication ticket is encrypted and added to the Cookies collection. Finally, the user is redirected to the homepage (Default.aspx ). If Page.User.Identity IsNot Nothing AndAlso TypeOf Page.User.Identity Is FormsIdentity Then Dim ident As FormsIdentity = CType(Page.User.Identity, FormsIdentity) Dim ticket As FormsAuthenticationTicket = ident.Ticket Dim AdminUserName As String = ticket.UserData If Not String.IsNullOrEmpty(AdminUserName) Then 'An Admin user is logged on as another user... 'The variable AdminUserName returns the Admin user's name 'To get the currently logged on user's name, use Page.User.Identity.Name Else 'The user logged on directly (the typical scenario) End If End IfFor more information on how to programmatically add and retrieve user data to a forms authentication ticket, see Forms Authentication Configuration and Advanced Topics.
~/MasterPage.master ) to include a Panel that displays a prominent message if an Admin user is currently logged in as another user. This message is shown only if an Admin user is logged on as another user; if a user logs on as himself, this message is not displayed. After some more research and trial-and-error, I think I found a solution by usingSystem.Collections.ArrayList. However, this does not work with getting a value by index. To do so, I created a new class ComArrayList that inherits from ArrayList and adds new methodsGetByIndex and SetByIndex.
public class ComArrayList : System.Collections.ArrayList {
public virtual object GetByIndex(int index) {
return base[index];
}
public virtual void SetByIndex(int index, object value) {
base[index] = value;
}
}
public class Department {
public string Code { get; private set; }
public string Name { get; private set; }
// ...
}
public ComArrayList GetDepartments() {
// return a List(of Department) from the database
}<h1>The third department</h1>
<%= departments.GetByIndex(2).Name %>
Array - represents an old-school memory array - kind of like a alias for a normal type[] array. Can enumerate. Can't grow automatically. I would assume very fast insertion and retriv. speed.ArrayList - automatically growing array. Adds more overhead. Can enum., probably slower than a normal array but still pretty fast. These are used a lot in .NET [utilizzabile per esporre a COM una List(of T): vedi questo altro post -- è inutile??? Basta ritornare un IList!!!!]List - one of my favs - can be used with generics, so you can have a strongly typed array, e.g.List<string>. Other than that, acts very much like ArrayList.Hashtable - plain old hashtable. O(1) to O(n) worst case. Can enumerate the value and keys properties, and do key/val pairs.Dictionary - same as above only strongly typed via generics, such as Dictionary<string, string>SortedList - a sorted generic list. Slowed on insertion since it has to figure out where to put things. Can enum., probably the same on retrieval since it doesn't have to resort, but deletion will be slower than a plain old list.List and Dictionary all the time - once you start using them strongly typed with generics, its really hard to go back to the standard non-generic ones. KeyValuePair which you can use to do some interesting things, there's a SortedDictionary which can be useful as well. USE AdventureWorks
GO
-- Check Table Column
SELECT Name
FROM HumanResources.Shift
GO
-- Get CSV values
SELECT SUBSTRING(
(SELECT ',' + s.Name
FROM HumanResources.Shift s
ORDER BY s.Name
FOR XML PATH('')),2,200000) AS CSV
GORisultati:Name -------------------------------------------------- Day Evening Night CSV -------------------------------------------------- Day,Evening,Night
Altro metodo:
DECLARE @a AS VARCHAR(4000) SET @a = '' SELECT @a = @a + Nome + ',' FROM Argomenti_tb SELECT @aRisultati:
--------------------------------------------------------------------------------- Notizie,Primo Piano,Galleria Fotografica,Argomento pubblico 1,Aromento Privato 1,
Altro metodo: COALESCE
DECLARE @fruitNames VARCHAR(8000) SELECT @fruitNames = COALESCE(@fruitNames + ', ', '') + FruitName FROM Fruits SELECT FruitNames = @fruitNamesRisultati:
FruitNames ‐‐‐‐‐‐‐‐‐‐ Apple, Orange, Mango, Banana, GrapeThe COALESCE function is used to ensure that there is no comma (,) after the last FruitName.
trovati qui.
Every once in a while when I'm exploring a technology, I experience a jaw-dropping moment where I'm blown away by how elegant, or well designed, or rife with potential something is. This weekend I had such a moment with ASP.NET 2.0 (yes, I know it was a holiday weekend, but I suddenly have a lot of deadlines looming). In preparation for a conference talk I'm giving at WinDev this year on compiliation in ASP.NET 2.0, I had a chance to try building my own custom build provider. As you may already know, when you place a source code file (like mycomponent.cs) under the top level /code directory it is compiled into an assembly and the rest of your pages can then reference the classes in that assembly. This is a nice feature in that it completes the delay-compile picture for deployment. In the ASP.NET 1.x you had to pre-compile any components you wanted to include and deploy them in the /bin directory (or find a page that wasn't using code behind and hijack its src= attribute, or use the assembly directive, each of which had its own set of issues). Now you can truly deploy nothing but .as*x and source files to a site, or even work in that mode and deploy with only binary.
What's even more interesting about the /code directory is that you can place other types of files in it as well, including .wsdl and .xsd files. Dropping a .wsdl file into /code will trigger a custom build step that involves generating a webservice proxy class which you then have immediate access to in your pages. VS.NET 2005 has really nice support for this too - as soon as you drop a .wsdl file into the directory you can use the object browser to view the generated proxy class, and you immediately have intellisense in all your pages for the proxy. Dropping a .xsd file into /code generates a typesafe DataSet-derived class like you may have used in the past when you ran the xsd.exe utility or selected 'Generate typesafe DataSet' from the designer.
Anyway, these are nice features, but this was not the jaw-dropping momemt for me. It turns out that you can build your own 'builder' and associate it with a specific extension. Once you deploy it, any file with your extension placed in the /code directory will then use your builder to spit out whatever code you want and compile it as part of the generated assembly for the /code directory. I have recently been working on building a data access layer for a client of mine, and for a number of reasons we chose not to go with typed DataSets, so instead I had written a code generator to extract schema information from a database connection and spit out a class with all the appropriate fields and properties based on the column types (actually similar to what the typed DataSet generator does, but without a lot of the other baggage). This seemed like a perfect candidate to test out as a custom builder, so I set to work.
The first step was to define the input file for my builder (this would be the file you place in the /code directory once I was done). I had an XML format I had been using, so I stuck with it and decided to use the extension of .dal for the file association. Here's a sample .dal file that would generate a class for the authors, publishers, discounts, and sales tables of the pubs database:
<!-- file: pubs.dal -->
<dalGenerator>
<connectionString>server=.;trusted_connection=yes;database=pubs</connectionString>
<namespace>MyDAL</namespace>
<tables>
<table>
<name>authors</name>
</table>
<table>
<name>publishers</name>
</table>
<table>
<name>discounts</name>
</table>
<table>
<name>sales</name>
</table>
</tables>
</dalGenerator>Now, to associate the .dal extension with a custom build provider, you add an entry to your web.config file that looks like:<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<compilation>
<buildProviders>
<add extension=".dal" appliesTo="Code" type="PS.DalBuildProvider" />
</buildProviders>
</compilation>
</system.web>
</configuration>
Where the PS.DalBuildProvider was the class I had yet to build. So the next step is obviously to go build the build provider class. To do this, you create a class the derives from the BuildProviderabstract base class, and most of the time you will just override the GenerateCode method which takes a reference to a AssemblyBuilder class as a paramter. Your job in this method is to create a newCodeCompileUnit (yes, you have to use the CodeDom) filled with whatever code you want compiled and add it to the AssemblyBuilder as a compile unit.
Here's a shell of such a class to give you an idea of what must be done - I've used theCodeSnippetCompileUnit in this sample so I can easily drop in a chunk of C# code. In the realDalBuildProvider I used the CodeDom classes (I'll provide a link to the complete sample below) which I recommend you do too for language independence.
using System;
using System.Text;
using System.Web.Compilation;
using System.Web;
using System.Web.UI;
using System.CodeDom;
using System.Web.Hosting;
using System.IO;
namespace PS
{
public class DalBuildProvider : BuildProvider
{
public override void GenerateCode(AssemblyBuilder ab)
{
// retrieve input file
string filename = base.VirtualPath;
XmlDocument doc = new XmlDocument();
using (Stream inFile = VirtualPathProvider.OpenFile(base.VirtualPath))
{
doc.Load(inFile);
}
// this is where I would navigate the DOM to generate code
string sampleCode = "class Foo {}";
ab.AddCodeCompileUnit(this,
new CodeSnippetCompileUnit(samplecode));
}
}
}
Once this class was built, I deployed it in my local /bin and added the pubs.dal file shown above to the /code directory, and it worked! Here's a screenshot of what the screen looked like when my jaw actually hit the floor :)
by fritz-onion.
Trovato qui.
What I required was all my messages for the application to be held in sys.messages and all my application defaults to be held in my table PortDefaults. In SQL they are available to other developers who want to run adhoc queries directly on the database, and to stored procedures and user defined functions which is the only access the asp.net application has to the database. When writing vb.net code for the application I required intellisense to offer me a list of the available messages and defaults for me to chose from so I did not need to constantly be refering back to my sql tables. Most of all I wanted all this to be fuss and maintenance free. If another developer had added a new set of messages, I wanted them to be available to me without any need to change other tables or code. If I added a new default to the database, I wanted it to show on my intellisense prompting.
The fabulous BuildProvider class in conjunction with the CodeDom allowed these goals to be acheived easily with considerable help from two excellent articles: Jaw-dropping experience with custom build providers by Friz Onion and Dino Esposito's Cutting Edge article.
SELECT message_id, CASE WHEN message_id < 60000 THEN 'Information' WHEN message_id < 70000 THEN 'Warning' WHEN message_id < 80000 THEN 'Error' END AS [group], text FROM sys.messages WHERE (message_id > 50000)
Namespace repository
Class SqlMessage
Enum Information
The_task_has_completed_successfully = 50001
Your_password_was_changed = 50002
End Enum
Enum Warning
Stock_of_this_item_is_now_low = 60001
This_supplier_will_not_deliver_at_weekends = 60002
Your_password_will_expire_in_PARM_days
End Enum
Enum [Error]
No_items_were_found = 70001
This_account_has_not_been_authorised = 70002
Your_password_has_expired = 70003
End Enum
End Class
End Namespace
We start by creating an XML file to hold information about our sql connection, data tables and columns and a few details about what we want to create. We will give the file an extension of .repos. Any unused extension will do, but the extension will be important later. The name of the file is not important. Our XML file will look similar to this ThetableName="PortMessagesView" numberColumnName="message_id" groupColumnName="group" textColumnName="text" className="SqlMessage"> tableName="PortDefaultsView" numberColumnName="uid" groupColumnName="group" textColumnName="name" className="PortDefaults">
'get the xml input file
Try
Dim filename As String = MyBase.VirtualPath
Dim xmlStream As Stream = VirtualPathProvider.OpenFile(MyBase.VirtualPath)
xmlFile.Load(xmlStream)
Catch ex As XPath.XPathException
System.Console.WriteLine("XML Exception:" & ex.Message)
Catch ex As Exception
System.Console.WriteLine("Exception:" & ex.Message)
End Try
'and create our navigator
navigator = xmlFile.CreateNavigator
'now on to the business of creating the code
'somewhere to put our code
Dim createdCode As New CodeCompileUnit
'create the namespace
Dim createdNamespace As New CodeNamespace
'and find its name and name it
Dim ns As String = ""
iterator = navigator.Select("/repositorys")
iterator.MoveNext()
ns = iterator.Current.GetAttribute("namespace", "")
If ns = "" Then
ns = "DefaultRepository"
System.Console.WriteLine("No namespace found - using default")
End If
createdNamespace.Name = ns
createdCode.Namespaces.Add(createdNamespace)
'add commentary
Dim comment As New CodeCommentStatement("This code has been generated by the message repository tool")
createdNamespace.Comments.Add(comment)
'now we iterate through the individual repository(s) pulling of the attributes we need to access the data
'so that we can enumerate the datarows
iterator = navigator.Select("/repositorys/repository")
Do While iterator.MoveNext
Dim cs As String = iterator.Current.GetAttribute("connectionString", "")
If cs = "" Then
System.Console.WriteLine("connectionString not specified for repository " & iterator.Current.Name)
Exit Sub
End If
'... and so on for our other attributes (tn(tablename), nc(numberColumn), gc(groupColumn) tc(textColumn) and cn(className) ...we now know what all our attributes are so we can go on to fill the namespace with a class using CodeTypeDeclaration. Then fill the class with one or more enums (depending on how many groups there are) using CodeTypeDeclaration with isEnum set true. Each enum is filled with declarations using CodeMemberField to create the field and CodePrimitiveExpression to set its value. The field name must comprise only alphas and underscores so a quick function filterName will clean the text up for use. Private Function filterName(ByVal source As String) As String
Dim filtered As String = ""
For Each letter As Char In source.ToCharArray
If Not Char.IsLetter(letter) Then
If letter = "%"c Then
filtered &= "PARM"
Else
letter = "_"c
filtered &= letter
End If
Else
filtered &= letter
End If
Next
Return filtered
End FunctionThe filtered function returns PARM in place of the percent sign, just to highlight that a parameter is expected for the message. Not perfect as it does not deal with escaped % signs, but adequate for our purposes. 'create our top level class with the classname
Dim messageClass As CodeTypeDeclaration = New CodeTypeDeclaration(cn)
messageClass.Name = cn
createdNamespace.Types.Add(messageClass) 'class is the default type
'now access the data
'get the data we need
Dim allDa As SqlDataAdapter = New SqlDataAdapter("select * from " & tn, cs)
Dim allDs As DataSet = New DataSet
allDa.Fill(allDs)
'and and a list of the distinct groups in the table which will become enums
Dim groupsDa As SqlDataAdapter = New SqlDataAdapter("select distinct [" & GC & "] from " & tn, cs)
Dim groupsDs As DataSet = New DataSet
groupsDa.Fill(groupsDs)
For Each group As DataRow In groupsDs.Tables(0).Rows 'zero is the only table
Dim currentGroup As String = group.Item(0) ' there is only column zero
'now create an enum for this group
Dim createEnum As CodeTypeDeclaration = New CodeTypeDeclaration(currentGroup)
createEnum.IsEnum = True 'need to specify enum for this type
'and add it to our message class
messageClass.Members.Add(createEnum)
'now fill it with declarations
For Each datarow As DataRow In allDs.Tables(0).Select("[" & GC & "]='" & currentGroup & "'")
'our field name is derived from the text, replacing punctuation with underscores using filterName function
Dim fieldName As String = filterName(datarow.Item(tc).ToString)
'and our value is the value form the numbercolumn
Dim fieldValue As Integer = CInt(datarow.Item(nc))
'create the field
Dim field As CodeMemberField = New CodeMemberField
field.Name = fieldName
field.InitExpression = New CodePrimitiveExpression(fieldValue)
'add to the current group enumeration
createEnum.Members.Add(field)
Next
Next
We now have everything in our CodeCompileUnit. Of course we have not done anything with it yet. Our next task is to get the code in our CodeCompileUnit to be made available to our application. For this we use the BuildProvider facilities available to asp.net. If you have never come across the BuildProvider before, then be warned - THIS REALLY IS AS EASY AS IT LOOKS!
Firstly we need to tell asp.net about our provider which we do in web.config. I have created a folder in my App_Code folder called CustomBuilders which is where I will put the builder. We specify this in So (at last!) it is time to bring things together by creating our custom builder namespace (CustomBuilders) containing our build provider (ReposBuilder) . We inherit the BuildProvider class and provide just one override for the GenerateCode method which will contain our code-generating code and a couple of lines to write out the code. So what do we get?
When you add you .repos file to the App_Code folder asp.net will see to the code creation for you. If you go to the vb code for a page and add an import, you will see (in our case) {}repository come up on the list. Having imported it you can use a simple statement like:
The extension attribute of .repos (or whatever extension you chose for your XML input file earlier on) is the wonderful thing about the build provider. Now, everytime you place a file with the .repos (or whatever) extension into the App_Code folder, the build provider will be triggered to generate the code you have specified. You won't see the code (just as you don't see so much of the code in asp.net 2.0) but its there, and as if by magic your newly generated namespace and classes will be there for you use.
Imports Microsoft.VisualBasic
Imports System
Imports System.IO
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.Hosting
Imports System.Web.Compilation
Imports System.CodeDom
Imports System.Xml
Imports System.Data
Imports System.Data.SqlClient
Namespace CustomBuilders
This vb file needs to be placed in the App_Code/CustomBuilders folder that we created earlier. No need to compile - nothing else required beyond this code, the web.config entries and our XML input file with the .repos extension in the App_Code folder.
dim t as integer = message.error.No_items_were_found
When you enter the dot after message, the intellisense will offer you Error|Information|Warning, and as you enter the dot after error, the intellisense dropdown offers you all you error messages. The variable t will be assigned the message number from you message table. The designer is even kind enough to put them all in alphabetical order for you!
Points of Interest
If, like me, you tend to shy away from some of the less obvious features of asp.net, because you don't have time to acquire the skills or feel that the return on the effort would not be worthwhile, think again when it comes to the BuildProvider. It really is so straightforward to use and even a simple application like this could reap gains in a very short time, not to mention improvements in consistency and reductions in maintenance.
By stewartamackenzie.
Trovato qui.
Imports System.Reflection.EmitThe code below is amazingly simple to use. Just set a few variables at the start of the code to your values and you are off. This code will create a new assembly, designated by the assemblyName variable, with a DLL extension, in the application folder.
OpenDatabase() 'you provide this
Try
Dim assemblyName As String = "DynEnum"
Dim lookupSQL As String = "SELECT ID, Name FROM AllMessageTypes"
Dim nameField As String = "Name"
Dim valueField As String = "ID"
Dim enumerationName As String = "MessageTypes"
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
Dim aName As AssemblyName = New AssemblyName(assemblyName)
Dim ab As AssemblyBuilder = currentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave)
Dim mb As ModuleBuilder = ab.DefineDynamicModule(aName.Name, aName.Name & ".dll")
Dim eb As EnumBuilder = mb.DefineEnum(enumerationName, TypeAttributes.Public, GetType(Integer))
'your saved connection
Dim cmd As New SqlCommand(lookupSQL, _Connection)
Dim dr As SqlDataReader = cmd.ExecuteReader
If dr.HasRows Then
Do While dr.Read
eb.DefineLiteral(dr.GetValue(dr.GetOrdinal(nameField)), dr.GetValue(dr.GetOrdinal(valueField)))
Loop
End If
dr.Close()
eb.CreateType()
ab.Save(aName.Name & ".dll")
Catch ex As Exception
Throw ex
End Try
From this point, all you have to do after you've run your application once and created the resulting DLL, is add the DLL to the references of your application. If you add or remove any values in the lookup table, it is automatically reflected in the enumeration, as it is generated by the application. I place this code in the constructor of the class that will consume the enumerations, although I don't believe that it matters where it's placed in relation to the rest of the application. If you find that's not the case, please let me know. Possible problems with this approach I haven't verified this yet, but there may be instances where anti-virus applications would see a change in a DLL during application execution as an infected file, so you may wish to keep an eye out for such a situation.
Take for example, the following loop:
string lookupValue;
using (SQLiteCommand cmd = cnn.CreateCommand())
{
for (int i = 0; i < 100; i++)
{
lookupValue = getSomeLookupValue(i);
cmd.CommandText = @"UPDATE [Foo] SET [Value] = [Value] + 1
WHERE [Customer] LIKE '" + lookupValue + "'";
cmd.ExecuteNonQuery();
}
}Now this code may look innocent enough, but it suffers some performance penalties and some security risks. First, the CommandText has to be re-evaluated every time the command is executed. SQLite must parse the statement and construct a query plan 100 times in this loop. There are also a lot of memory allocations being done here. The previously-prepared CommandText is freed, the new CommandText allocated. A statement is compiled and strings are concatenated causing even more allocations and deallocations. There are also a great number of interop calls being performed behind the scenes.
SQLite supports named and unnamed parameters. Named parameters must appear in the SQL statement with either a $ (dollar), :(colon) or @ (at sign) prefix. Unnamed parameters consist of a single question mark ? character. Rewriting the above code to use a named parameter looks like this:
using (SQLiteCommand cmd = cnn.CreateCommand())
{
cmd.CommandText = @"UPDATE [Foo] SET [Value] = [Value] + 1
WHERE [Customer] LIKE @lookupValue";
SQLiteParameter lookupValue = new SQLiteParameter("@lookupValue");
cmd.Parameters.Add(lookupValue);
for (int i = 0; i < 100; i++)
{
lookupValue.Value = getSomeLookupValue(i);
cmd.ExecuteNonQuery();
}
}The same code now using an unnamed parameter (compatible with Jet/Access)
using (SQLiteCommand cmd = cnn.CreateCommand())
{
cmd.CommandText = @"UPDATE [Foo] SET [Value] = [Value] + 1
WHERE [Customer] LIKE ?";
SQLiteParameter lookupValue = new SQLiteParameter();
cmd.Parameters.Add(lookupValue);
for (int i = 0; i < 100; i++)
{
lookupValue.Value = getSomeLookupValue(i);
cmd.ExecuteNonQuery();
}
} Using trans As DbTransaction = Cnn.BeginTransaction() .... trans.Commit() End Using or Dim trans As DbTransaction Try trans = Cnn.BeginTransaction() .... trans.Commit() Finally If trans IsNot Nothing Then trans.Dispose() End TrySQLite is somehow special regarding transactions. SQLite executes much faster one single large transaction than many small transactions. This is a SQLite characteristic, other database engines might behave different.
Trovato qui.
Most SQL database engines (every SQL database engine other than SQLite, as far as we know) uses static typing. With static typing, the datatype of a value is determined by its container - the particular column the value is stored in.
SQLite uses a more general dynamic type system. In SQLite, the datatype of a value is associated with the value itself, not with the container in which it is stored. The dynamic type system of SQLite is backwards compatible with the more common static type systems of other database engines in the sense that SQL statement that work on statically typed databases should would the same way in SQLite. However, the dynamic typing in SQLite allowed it to do things which are not possible in traditional statically typed databases.
Each value stored in an SQLite database (or manipulated by the database engine) has one of the following storage classes:
Any column in a version 3 database, except an INTEGER PRIMARY KEY column, may be used to store any type of value.
All values supplied to SQLite, whether as literals embedded in SQL statements or values bound to pre-compiled SQL statements are assigned a storage class before the SQL statement is executed. Under circumstances described below, the database engine may convert values between numeric storage classes (INTEGER and REAL) and TEXT during query execution.
Storage classes are initially assigned as follows:
The storage class of a value that is the result of an SQL scalar operator depends on the outermost operator of the expression. User-defined functions may return values with any storage class. It is not generally possible to determine the storage class of the result of an expression at compile time.
In SQLite version 3, the type of a value is associated with the value itself, not with the column or variable in which the value is stored. (This is sometimes called manifest typing or duck typing.) All other SQL databases engines that we are aware of use the more restrictive system of static typing where the type is associated with the container, not the value. To look at it another way, SQLite provides dynamic datatypes such as one finds in "script" programming languages such as Awk, Perl, Tcl, Python, and Ruby, whereas other SQL database engines provide only compile-time fixed, static typing such as found in Pascal, C++, and Java.
In order to maximize compatibility between SQLite and other database engines, SQLite support the concept of "type affinity" on columns. The type affinity of a column is the recommended type for data stored in that column. The key here is that the type is recommended, not required. Any column can still store any type of data, in theory. It is just that some columns, given the choice, will prefer to use one storage class over another. The preferred storage class for a column is called its "affinity".
Each column in an SQLite 3 database is assigned one of the following type affinities:
A column with TEXT affinity stores all data using storage classes NULL, TEXT or BLOB. If numerical data is inserted into a column with TEXT affinity it is converted to text form before being stored.
A column with NUMERIC affinity may contain values using all five storage classes. When text data is inserted into a NUMERIC column, an attempt is made to convert it to an integer or real number before it is stored. If the conversion is successful (meaning that the conversion occurs without loss of information), then the value is stored using the INTEGER or REAL storage class. If the conversion cannot be performed without loss of information then the value is stored using the TEXT storage class. No attempt is made to convert NULL or blob values.
A column that uses INTEGER affinity behaves in the same way as a column with NUMERIC affinity, except that if a real value with no fractional component and a magnitude that is less than or equal to the largest possible integer (or text value that converts to such) is inserted it is converted to an integer and stored using the INTEGER storage class.
A column with REAL affinity behaves like a column with NUMERIC affinity except that it forces integer values into floating point representation. (As an internal optimization, small floating point values with no fractional component are stored on disk as integers in order to take up less space and are converted back into floating point as the value is read out.)
A column with affinity NONE does not prefer one storage class over another. No attempt is made to coerce data from one storage class into another. The data is stored on disk exactly as specified.
The type affinity of a column is determined by the declared type of the column, according to the following rules:
If a table is created using a "CREATE TABLE <table> AS SELECT..." statement, then all columns have no datatype specified and they are given no affinity.
CREATE TABLE t1(
t TEXT,
nu NUMERIC,
i INTEGER,
no BLOB
);
-- Storage classes for the following row:
-- TEXT, REAL, INTEGER, TEXT
INSERT INTO t1 VALUES('500.0', '500.0', '500.0', '500.0');
-- Storage classes for the following row:
-- TEXT, REAL, INTEGER, REAL
INSERT INTO t1 VALUES(500.0, 500.0, 500.0, 500.0);Like SQLite version 2, version 3 features the binary comparison operators '=', '<', '<=', '>=' and '!=', an operation to test for set membership, 'IN', and the ternary comparison operator 'BETWEEN'.
The results of a comparison depend on the storage classes of the two values being compared, according to the following rules:
SQLite may attempt to convert values between the numeric storage classes (INTEGER and REAL) and TEXT before performing a comparison. Whether or not any conversions are attempted before the comparison takes place depends on the nominal affinity assigned to the expressions on either side of the binary operator. Affinities are assigned to expressions in the following cases:
Conversions are applied before the comparison as described below. In the following bullet points, the two operands are refered to as expression A and expression B. Expressions A and B may appear as either the left or right operands - the following statements are true when considering both "A <op>B" and "B <op>A".
In SQLite, the expression "a BETWEEN b AND c" is equivalent to "a >= b AND a <= c", even if this means that different affinities are applied to 'a' in each of the comparisons required to evaluate the expression.
Expressions of the type "a IN (SELECT b ....)" are handled by the three rules enumerated above for binary comparisons (e.g. in a similar manner to "a = b"). For example if 'b' is a column value and 'a' is an expression, then the affinity of 'b' is applied to 'a' before any comparisons take place.
SQLite treats the expression "a IN (x, y, z)" as equivalent to "a = +x OR a = +y OR a = +z". The values to the right of the IN operator (the "x", "y", and "z" values in this example) are considered to be expressions, even if they happen to be column values. If the value of the left of the IN operator is a column, then the affinity of that column is used. If the value is an expression then no conversions occur.
CREATE TABLE t1(
a TEXT,
b NUMERIC,
c BLOB
);
-- Storage classes for the following row:
-- TEXT, REAL, TEXT
INSERT INTO t1 VALUES('500', '500', '500');
-- 60 and 40 are converted to '60' and '40' and values are compared as TEXT.
SELECT a < 60, a < 40 FROM t1;
1|0
-- Comparisons are numeric. No conversions are required.
SELECT b < 60, b < 600 FROM t1;
0|1
-- Both 60 and 600 (storage class NUMERIC) are less than '500'
-- (storage class TEXT).
SELECT c < 60, c < 600 FROM t1;
0|0All mathematical operators (which is to say, all operators other than the concatenation operator "||") apply NUMERIC affinity to all operands prior to being carried out. If one or both operands cannot be converted to NUMERIC then the result of the operation is NULL.
For the concatenation operator, TEXT affinity is applied to both operands. If either operand cannot be converted to TEXT (because it is NULL or a BLOB) then the result of the concatenation is NULL.
When values are sorted by an ORDER by clause, values with storage class NULL come first, followed by INTEGER and REAL values interspersed in numeric order, followed by TEXT values usually in memcmp() order, and finally BLOB values in memcmp() order. No storage class conversions occur before the sort.
When grouping values with the GROUP BY clause values with different storage classes are considered distinct, except for INTEGER and REAL values which are considered equal if they are numerically equal. No affinities are applied to any values as the result of a GROUP by clause.
The compound SELECT operators UNION, INTERSECT and EXCEPT perform implicit comparisons between values. Before these comparisons are performed an affinity may be applied to each value. The same affinity, if any, is applied to all values that may be returned in a single column of the compound SELECT result set. The affinity applied is the affinity of the column returned by the left most component SELECTs that has a column value (and not some other kind of expression) in that position. If for a given compound SELECT column none of the component SELECTs return a column value, no affinity is applied to the values from that column before they are compared.
The above sections describe the operation of the database engine in 'normal' affinity mode. SQLite version 3 will feature two other affinity modes, as follows:
By default, when SQLite compares two text values, the result of the comparison is determined using memcmp(), regardless of the encoding of the string. SQLite v3 provides the ability for users to supply arbitrary comparison functions, known as user-defined "collation sequences" or "collating functions", to be used instead of memcmp().
Aside from the default collation sequence BINARY, implemented using memcmp(), SQLite features two extra built-in collation sequences intended for testing purposes, the NOCASE and RTRIM collations:
Each column of each table has a default collation type. If a collation type other than BINARY is required, a COLLATE clause is specified as part of the column definition to define it.
Whenever two text values are compared by SQLite, a collation sequence is used to determine the results of the comparison according to the following rules. Sections 3 and 5 of this document describe the circumstances under which such a comparison takes place.
For binary comparison operators (=, <, >, <= and >=) if either operand is a column, then the default collation type of the column determines the collation sequence to use for the comparison. If both operands are columns, then the collation type for the left operand determines the collation sequence used. If neither operand is a column, then the BINARY collation sequence is used. For the purposes of this paragraph, a column name preceded by one or more unary "+" operators is considered a column name.
The expression "x BETWEEN y and z" is equivalent to "x >= y AND x <= z". The expression "x IN (SELECT y ...)" is handled in the same way as the expression "x = y" for the purposes of determining the collation sequence to use. The collation sequence used for expressions of the form "x IN (y, z ...)" is the default collation type of x if x is a column, or BINARY otherwise.
An ORDER BY clause that is part of a SELECT statement may be assigned a collation sequence to be used for the sort operation explicitly. In this case the explicit collation sequence is always used. Otherwise, if the expression sorted by an ORDER BY clause is a column, then the default collation type of the column is used to determine sort order. If the expression is not a column, then the BINARY collation sequence is used.
The examples below identify the collation sequences that would be used to determine the results of text comparisons that may be performed by various SQL statements. Note that a text comparison may not be required, and no collation sequence used, in the case of numeric, blob or NULL values.
CREATE TABLE t1(
a, -- default collation type BINARY
b COLLATE BINARY, -- default collation type BINARY
c COLLATE REVERSE, -- default collation type REVERSE
d COLLATE NOCASE -- default collation type NOCASE
);
-- Text comparison is performed using the BINARY collation sequence.
SELECT (a = b) FROM t1;
-- Text comparison is performed using the NOCASE collation sequence.
SELECT (d = a) FROM t1;
-- Text comparison is performed using the BINARY collation sequence.
SELECT (a = d) FROM t1;
-- Text comparison is performed using the REVERSE collation sequence.
SELECT ('abc' = c) FROM t1;
-- Text comparison is performed using the REVERSE collation sequence.
SELECT (c = 'abc') FROM t1;
-- Grouping is performed using the NOCASE collation sequence (i.e. values
-- 'abc' and 'ABC' are placed in the same group).
SELECT count(*) GROUP BY d FROM t1;
-- Grouping is performed using the BINARY collation sequence.
SELECT count(*) GROUP BY (d || '') FROM t1;
-- Sorting is performed using the REVERSE collation sequence.
SELECT * FROM t1 ORDER BY c;
-- Sorting is performed using the BINARY collation sequence.
SELECT * FROM t1 ORDER BY (c || '');
-- Sorting is performed using the NOCASE collation sequence.
SELECT * FROM t1 ORDER BY c COLLATE NOCASE;