Sunday, November 26, 2006

How to use BLOB field in VB 6 with Firebird

As per this post on Devshed forums I'm pasting here some sample code (taken from Mike Hillyer's excellent "Accessing MySQL BLOB columns using Visual Basic 6"), hope it helps.
Code uses a simpler table structure and due to the fact that I didn't bother about reproducing the "autoincrement" field it will get you into troubles if trying to run the sample multiple times.
You'll notice that basically the only different thing is the connection string!

  1. 'CREATE CONNECTION OBJECT AND ASSIGN CONNECTION STRING
  2. Dim conn As ADODB.Connection
  3. Set conn = New ADODB.Connection
  4. conn.ConnectionString = "DRIVER={Firebird/Interbase(r) Driver};DBNAME=localhost:C:\Programmi\Firebird\Firebird_2_0\examples\empbuild\test.fdb;UID=SYSDBA;PW D=masterkey"
  5. conn.CursorLocation = adUseClient
  6. conn.Open
  7. 'OPEN RECORDSET FOR WRITING
  8. Dim rs As ADODB.Recordset
  9. Set rs = New ADODB.Recordset
  10. Dim mystream As ADODB.Stream
  11. Set mystream = New ADODB.Stream
  12. mystream.Type = adTypeBinary
  13. rs.Open "SELECT * FROM files WHERE 1=0", conn, adOpenStatic, adLockOptimistic
  14. rs.AddNew
  15. mystream.Open
  16. mystream.LoadFromFile "c:\adaptor.jpg"
  17. rs!file_id = 1
  18. rs!file_name = "adaptor.jpg"
  19. rs!file_size = mystream.Size
  20. rs!file_blob = mystream.Read
  21. rs.Update
  22. mystream.Close
  23. rs.Close
  24. 'OPEN RECORDSET TO READ BLOB
  25. rs.Open "Select * from files WHERE files.file_id = 1", conn
  26. mystream.Open
  27. mystream.Write rs!file_blob
  28. mystream.SaveToFile "c:\newimage.jpg", adSaveCreateOverWrite
  29. mystream.Close
  30. rs.Close
  31. 'OPEN RECORDSET FOR UPDATE OF BLOB COLUMN
  32. rs.Open "Select * from files WHERE files.file_id = 1", conn, adOpenStatic, adLockOptimistic
  33. mystream.Open
  34. mystream.LoadFromFile "c:\adaptor.jpg"
  35. rs!file_blob = mystream.Read
  36. rs.Update
  37. mystream.Close
  38. rs.Close
  39. 'OPEN RECORDSET TO READ UPDATED IMAGE
  40. rs.Open "Select * from files WHERE files.file_id = 1", conn
  41. mystream.Open
  42. mystream.Write rs!file_blob
  43. mystream.SaveToFile "c:\newupdatedimage.jpg", adSaveCreateOverWrite
  44. mystream.Close
  45. rs.Close
  46. conn.Close
  47. MsgBox "Success! Check your C:\ directory for newimage.jpg and newupdatedimage.jpg"

Wednesday, November 08, 2006

Custom ordering for your results

... or, taming the ORDER BY clause.
Say you want to implement a custom ordering for your queries, as an example, you want to display each customer's orders with shipped orders first, then waiting orders and open orders last.
That's ordering by the 'status' column and you can't use alphabetical ordering!
In this case you'll have to implement some kind of logic in your order by clause, and a CASE is very handy, like this:

  1. SELECT * FROM SALES
  2. ORDER BY
  3. cust_no,
  4. CASE WHEN order_status = 'shipped' THEN 1
  5. WHEN order_status = 'waiting' THEN 2
  6. WHEN order_status = 'open' THEN 3
  7. ELSE 4
  8. END;

Note how the ordering is performed on the values substituted by the CASE statement and not
for the original column values.
This example is based on Firebird's EXAMPLE.FDB database.

The MySQL guys could also use a trick like the FIELD function

... ORDER BY FIELD(order_status,'shipped','waiting','open')

but this is a NON standard way of doing things that I don't recommend at all.

Thursday, November 02, 2006

Of correlated and uncorrelated subqueries

Many SQL questions on Devshed forums are easily solved using correlated and uncorrelated subqueries, but many don't know of them at all, or at least are not shure about the syntax, usage and performance implications, they sometimes rave about a stored procedure to loop and select other values ...
The definition (taken from O'Reilly's "Learning SQL on SQL Server 2005") of a correlated subquery:
A correlated subquery is an inner subquery whose information is referenced by the main, outer query such that the inner query may be thought of as being executed repeatedly.
And the definition of an uncorrelated subquery:
A noncorrelated subquery is a subquery that is independent of the outer query. In other words, the subquery could be executed on its own.
This is all fine and dandy, but what's the use?
I'll put up two simple examples of usage, which should make clear which kind of questions can be answered with (un)correlated subqueries:

Example 1: get the number of "open" orders, the number of "waiting" orders and the open vs. waiting ratio

  1. SELECT
  2. COUNT(*) AS o_open,
  3. (SELECT COUNT(*) FROM sales WHERE order_status = 'waiting') AS o_waiting,
  4. (COUNT(*))/
  5. (SELECT COUNT(*) FROM sales WHERE order_status = 'waiting') AS open_waiting_ratio
  6. FROM SALES WHERE order_status = 'open';

You can see that the queries are uncorrelated, and they better be, as the where conditions are different.

Example 2: we want to see the customer number, the amount of the customer's last order and the total amount ordered by each customer

  1. SELECT
  2. s.cust_no,
  3. s.total_value AS order_value,
  4. (SELECT SUM(i_s.total_value) FROM sales i_s WHERE i_s.cust_no = s.cust_no) total_ordered
  5. FROM sales s
  6. WHERE
  7. s.order_date =
  8. (SELECT MAX(ii_s.order_date) FROM sales ii_s WHERE ii_s.cust_no = s.cust_no);

You can see that both subqueries are correlated, the first one gets total amount ordered for each customer, the second one gets the last order date (field is datetime) and helps determine the last order in the main query.

This should make you aware that some kind of set based logic is often disguised as procedural and usually leads to ugly cursor usage in stored procedures instead of direct queries by the inexperienced.

Another example of a correlated subquery to display customers and one randomly selected purchase order for each of them:

  1. SELECT
  2. a.CUST_NO,
  3. a.CUSTOMER,
  4. b.PO_NUMBER
  5. FROM
  6. CUSTOMER a
  7. INNER JOIN
  8. sales b
  9. ON a.CUST_NO = b.CUST_NO
  10. WHERE
  11. b.PO_NUMBER =
  12. (
  13. SELECT
  14. first 1 c.PO_NUMBER
  15. FROM
  16. sales c
  17. WHERE
  18. c.CUST_NO = b.CUST_NO
  19. ORDER BY
  20. rand()
  21. )


Both examples are run against Firebird's standard EMPLOYEE.FDB.


Correlated subqueries are also useful to specify updates and deletes on a table conditioned to values of another table (instead of joining them) see this example:

  1. UPDATE
  2. stops s
  3. SET
  4. s.inservice=1
  5. WHERE EXISTS
  6. (
  7. SELECT
  8. ls.stopid
  9. FROM
  10. linestop ls
  11. WHERE
  12. s.stopid=ls.stopid
  13. AND
  14. ls.signid=79
  15. );