SQL Blog of Zach Stagers

www.scratchbox.co.uk

Returning Tabular Data as XML with FOR XML EXPLICIT

No Comments »

FOR XML EXPLICIT can be a little awkward to use to say the least, but it has it uses. The good thing about EXPLICIT, is that you can mold it into almost any structure you like.

I actually found that reviewing the results as tabular data is what really helped me understand what the FOR XML EXPLICIT query is doing.

Below is the example code that I’m using:

SELECT
	1 AS Tag,
	NULL AS Parent,
	Name AS "Person!1!Name",
	PersonId AS "Person!1!PersonId",
	NULL AS "Orders!2!Item"
FROM Person

UNION ALL

SELECT
	2 AS Tag,
	1 AS Parent,
	NULL AS "Person!1!Name",
	PersonId AS "Person!1!PersonId",
	Item AS "Orders!2!Item"

FROM Orders

ORDER BY "Person!1!PersonId"

FOR XML EXPLICIT, ROOT('People')

The first thing to note with this query, is the two columns “tag” and “parent”. You must specify these columns in order to use FOR XML EXPLICIT. These columns specify the hierarchy of your results. The first query will have a “tag” of 1, and a NULL “parent”, as it is the first object declared. Each subsequent “tag” and “parent” will then go up by increments of 1.

Another thing to note, is the column names. The value listed before the first exclamation in the column name specifies the Element Name. The value listed before the second exclamation is the “tag” that it relates to. Then the attribute name is specified. ElementName!TagNumber!AttributeName.

Lastly, it’s the ORDER BY "Person!1!PersonId" that really gives the final result its correct structure.

The XML outputted by the query specified above is as follows:

<People>
  <Person Name="Zach Stagers" PersonId="1">
    <Orders Item="Bike" />
    <Orders Item="Laptop" />
    <Orders Item="Phone" />
    <Orders Item="Keyboard" />
    <Orders Item="Mouse" />
    <Orders Item="Pizza" />
  </Person>
  <Person Name="Libbie Coulson" PersonId="2" />
</People>

As with RAW and AUTO, the ROOT() simply specifies the name of the root element. This is optional.

Without the aforementioned ORDER BY clause, all of the orders for person “Zach” would appear under “Libbie” as she is last in the list, and the UNION ALL will just tack the results from the second query to the end of the result. Ordering by the key between the two tables ensures that the orders against PersonId 1 appear under the person with PersonId 1.

As I mentioned earlier in this post, what really helped me understand what was happening with FOR XML EXPLICIT was seeing the results in there tabular format. To do this, simply remove the FOR XMLline and re-run the code. In the below tabular example, I’ve removed FOR XML EXPLICIT, ROOT('People') from the query:

FOR XML EXPLICIT - Tabular Result Example

Using BULK INSERT

No Comments »

Using BULK INSERT is a nice and efficient way to, well, do a bulk insert! It’s can be good in situations where you have many, many records to insert – to many to fit into Excel, for example.

To do a BULK INSERT, simply upload your file to the server that holds the database you’re going to be inserting into. Put the file in a relatively simple location, unless you like typing long file paths! Alternatively, you can copy and paste the file path, of course.

Below is the example file I’ll be using, along with the structure of the table…

Below is the structure of the example table I’ll be using, along with the contents of the file which will be imported.

--Table

CREATE TABLE dbo.Orders
(
OrderId INT NOT NULL,
PersonId INT NOT NULL,
Item VARCHAR(50) NOT NULL
)

--Contents of Orders.txt

4, 1, Keyboard
5, 1, Mouse

Once your file is in place, you’re ready to roll.

BULK INSERT dbo.Orders
FROM 'C:\Orders.txt'
WITH
(
FIELDTERMINATOR = ','
ROWTERMINATOR = '\n'
)

Notice the WITH statment after specifying the file location. I have only used a couple of the options available to the BULK INSERT statement. I will explain these, but a for a full list of options – see the MSDN site on BULK INSERT.

The two options I’ve used in the example are the only two that you should need to insert some relatively simple data.

FIELDTERMINATOR is used to specify the character used to split your data into the columns they belong to, you need to have the columns in the correct order in the file so that they are mapped correctly. Obviously, you need to pick a character that isn’t going to appear in any of the columns.

ROWTERMINATOR is used to specify what denotes a new row. In my example, I’ve used \n, which is equal to an enviromental newline. This tells SQL that any time there is a new line in the Orders.txt file, that this should be treated as a seperate row from the line above.

Returning Tabular Data as XML with FOR XML AUTO

No Comments »

About a week ago I posted on FOR XML RAW, this time it’s FOR XML AUTO‘s turn.

The two are actioned via very similar syntax, but operate slightly differently. Auto is probably named “Auto” because it performs a lot of the XML node naming for you. There are also a couple of other differences which I’ll explain in this post.

With FOR XML AUTO, you don’t have the ability to name your row’s like you do in RAW. This is because their names are derived from the aliases given to the tables in your query. For example, having a table named Person, and using FOR XML AUTO against this table would give your rows a name of “Person”. This is the same as using FOR XML RAW('Person'), see below for a literal example of this:

SELECT * FROM Person
FOR XML AUTO

SELECT * FROM Person
FOR XML RAW('Person')

Both of the above examples would return the below XML:

<Person PersonId="1" Name="Zach Stagers" Age="22" />

As with RAW, you can specify your root element in AUTO:

SELECT * FROM Person
FOR XML AUTO, ROOT('People')

The above would return the exact same XML data, but with the specified root element wrapped around it.

<People>
  <Person PersonId="1" Name="Zach Stagers" Age="22" />
</People>

The attributes of the person row can further be split out into their own elements by specifying ELEMENTS, and can also be given a namespace and handle SQL NULL‘s by specifying XSINIL

SELECT * FROM Person
FOR XML AUTO, ROOT('People'), ELEMENTS, XSINIL

The above example would return:

<People xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Person>
    <PersonId>1</PersonId>
    <Name>Zach Stagers</Name>
    <Age>22</Age>
  </Person>
</People>

There are a couple of other things to remember when using FOR XML AUTO, and that is that the order of your XML elements is down the order in which your columns are specified. For example using the below two queries to return tabular data would give you the same results, but using them with FOR XML AUTO will give you completely different XML:

SELECT Person.Name,
       Orders.Item
FROM Person JOIN
     Orders ON Orders.PersonId = Person.PersonId
FOR XML AUTO, ROOT('PeopleWithOrders')

Returns:

<PeopleWithOrders>
  <Person Name="Zach Stagers">
    <Orders Item="Bike" />
    <Orders Item="Laptop" />
    <Orders Item="Phone" />
  </Person>
</PeopleWithOrders>
SELECT Orders.Item,
       Person.Name
FROM Person JOIN
     Orders ON Orders.PersonId = Person.PersonId
FOR XML AUTO, ROOT('PeopleWithOrders')

The above query, with the columns swapped over will return:

<PeopleWithOrders>
  <Orders Item="Bike">
    <Person Name="Zach Stagers" />
  </Orders>
  <Orders Item="Laptop">
    <Person Name="Zach Stagers" />
  </Orders>
  <Orders Item="Phone">
    <Person Name="Zach Stagers" />
  </Orders>
</PeopleWithOrders>

So, you just need to remember that the order of the columns is what specifies the output of your XML, not the order of the tables in the FROM clause. The names given to the tables, however, specifies the names given to the XML nodes.

Working with Optional Parameters in Stored Procedures

No Comments »

Using optional parameters within a stored procedure is a good way to give the caller a little more control over what is being returned.

For example, if you were writing a stored procedure which was going to be used by a reporting system, and within the system, a user will have the ability to narrow down a search by choosing options from various drop down lists. Instead of writing many different stored procedures to handle each of the different drop down lists, you can do it all within one using optional parameters.

An “optional parameter” is a parameter which is set to NULL by default. The parameter must be set to NULL by default

CREATE PROCEDURE dbo.GetSearchResults

@Franchise	VARCHAR(20) = NULL
@Make		VARCHAR(20) = NULL
@Colour		VARCHAR(20) = NULL

AS

SELECT Franchise,
       Make,
       Colour

FROM Vehicles

WHERE (@Franchise IS NULL OR Franchise = @Franchise) AND
      (@Make IS NULL OR Make = @Make) AND
      (@Colour IS NULL OR Colour = @Colour)

In the above example, if a value isn’t passed in for one of the parameters, then the default of NULL is used. This means that the first condition in the OR is true because the variable is NULL, and execution continues on to the next line.

The above stored procedure can also be called in a few different ways:

1)

EXEC dbo.GetSearchResults

This method simply uses all of the default values for each parameter, and the query results aren’t filtered in the where clause at all

2)

EXEC dbo.GetSearchResults 'Volkswagen', 'Golf'

This execution method uses the values passed in and assigns them to the parameter in the same position that the parameter is declared. By that I mean, @Franchise is declared first in the procedure, so Volkswagen would be assigned to this variable as it’s passed in first. This method is a little dangerous, as if the value intended for @Colour is passed in first, no results will be returned.

3)

EXEC dbo.GetSearchResults @Franchise = 'Volkswagen', @Colour = 'Red'

This method is probably the safest of the bunch, as it explicitly defines the value to the parameter. Any parameters that aren’t defined in the EXEC use the default value set in the procedrue.

Finding a list of user defined tables with schemas within a SQL Database

No Comments »

A couple of months ago I did a blog post on Finding a list of user defined tables within a SQL Database. Well, today I’ve improved upon that example to include the schema that the table belongs too as well.

USE database
SELECT S.name + '.' + O.name [SchemaTable]

FROM sys.all_objects O JOIN
     sys.schemas S ON S.schema_id = O.schema_id

WHERE type = 'U' AND
      is_ms_shipped = 0 AND
      S.schema_id IN (5, 6, 7, 8, 9)

ORDER BY O.name

The reason I needed a list of tables and the schemas they belong to in this format was because I needed to add two columns to each of them.

I’m far to lazy to open up each of these tables (there were 50 in total) and add the two columns manually, so I used the above SQL, as well as some concatenation to write the query for me:

USE database
SELECT 'ALTER TABLE ' + S.name + '.' + O.name + ' ADD Child VARCHAR(100) NULL,
       GrandChild VARCHAR(100) NULL' [SQL]

FROM sys.all_objects O JOIN
     sys.schemas S ON S.schema_id = O.schema_id

WHERE type = 'U' AND
      is_ms_shipped = 0 AND
      S.schema_id IN (5, 6, 7, 8, 9)

ORDER BY O.name 

I was then able to copy the results into a new query window and execute, saving myself a lot of time.

SQL LIKE ESCAPE Character

No Comments »

If you need to do a search on a field for a character that’s used by the LIKE syntax, the % for example, then you can specify an ESCAPE Character to allow you search for the symbol.

To do this, simply specify your escape character, typically a value which you know won’t be returned in the result set, before the character you’re searching for.

For example, if a column containing someones tax band was created as a VarChar, instead of a TinyInt and users were inserting the % symbol along with the rate you might want to find and correct these records before updating the columns data type. To do something like this, you would use a query like the below:

SELECT FirstName,
       Surname,
       TaxBand

WHERE TaxBand LIKE '%=%%' ESCAPE '='

This query specifies = as our ESCAPE symbol, therefore the % symbol listed after it can be searched for.