A technical blog about my projects, challenges, and discoveries in the world of data warehousing using SQL Server, Power BI Desktop, DevExpress, and more.
About Me
Monday, November 9, 2009
Registrations - Key Information
Thursday, October 22, 2009
Richmond Code Camp
- jQuery has nothing to do with SQL. I thought I was going to learn a bit about how to query a database source from a web page, but that was not it at all. JQuery is a cool add-on for JavaScript, but it has nothing particular to do with querying databases.
- SharePoint is much more powerful (and much more complex) than I gave it credit for. I went to two sessions on SharePoint, and I am very impressed with its capabilities. However, I am also quite certain that I am far away from ever tapping its full potential.
- I picked up the word "trivial" as a descriptor for solutions that do not require much effort to implement -- and the very important "non-trivial" descriptor for solutions that do.
- I saw Microsoft Azure for the first time, the cloud-computing solution for developers that is currently in development itself but will be rolling out in production next year. It is quite exciting, especially with the cloud-computing possibilities for my own company's applications.
- I saw Microsoft's Project Gemini for the first time, a new enhancement for Excel that will do for database querying what PivotTables did for spreadsheets. The presenter was one of the developers from Microsoft itself. He shows Excel pulling in millions (!) of rows of data and then analyzing it PivotTable fashion almost instantly. He showed how Gemini can correlate ad hoc data with query data. He showed how SQL Server Reporting Services reports can serve as a data source so that users can re-package their own reports based off of the underlying query that feeds the SSRS report. He blew me away. I can't wait for this feature in the next version of Excel. And I am curious as to whether Excel Server might be a useful tool for some of our customers. It is just so exciting what Gemini is going to bring in terms of user-level Business Intelligence options.
Tuesday, October 20, 2009
To Be Continued
- You are using GroupHeader1 as the "per page" grouping level. If you are using another Group level, substitute it for GroupHeader1 as needed.
- You have changed the NewPage property for GroupFooter1 to 2 - After.
- The PageFooter section remains as part of the design, and it is where the "Continued" message should print.
- a Bound Control
- (Name) = ctlCurrentPage
- SummaryGroup = GroupHeader1
- SummaryRunning = 1 - ddSRGroup
- SummaryType = 4 - ddSMPageCount
- Visible = False
- a second Bound Control
- (Name) = ctlTotalPages
- SummaryGroup = GroupHeader1
- SummaryRunning = 0 - ddSRNone
- SummaryType = 4 - ddSMPageCount
- Visible = False
- a Label
- (Name) = lblContinued
- Caption = Continued on Next Page
Sub OnBeforePrint
rpt.lblContinued.Visible = Not Eval("rpt.ctlCurrentPage.DataValue = rpt.ctlTotalPages.DataValue")
End Sub
Monday, October 19, 2009
Calculating Age in T-SQL
select NameCounter = n.NameCounter, Age = datediff(year, n.Birthdate, getdate())from Shelby.NANames as n
select NameCounter = n.NameCounter, Age = datediff(year, n.Birthdate, getdate()) - case when month(n.Birthdate) > month(getdate()) then 1 else 0 endfrom Shelby.NANames as n
select NameCounter = n.NameCounter, Age = datediff(year, n.Birthdate, getdate()) - case when month(n.Birthdate) > month(getdate()) or (month(n.Birthdate) = month(getdate()) and day(n.Birthdate) > day(getdate())) then 1 else 0 endfrom Shelby.NANames as n
select NameCounter = n.NameCounter, Age = case when year(n.Birthdate) = 1796 then null else datediff(year, n.Birthdate, getdate()) - case when month(n.Birthdate) > month(getdate()) or (month(n.Birthdate) = month(getdate()) and day(n.Birthdate) > day(getdate())) then 1 else 0 end endfrom Shelby.NANames as n
Friday, September 25, 2009
OnFormat vs. OnBeforePrint in Active Reports
- Use OnFormat to work with bound controls before their values are aggregated.
- Use OnBeforePrint to work with bound controls after their values are aggregated.
objValue = rpt.Sections("Detail").Controls.Item("Field1") objValue = rpt.Sections("Detail").Controls.Item("Field1").DataValue
Friday, September 18, 2009
Enumerating Options using VBScript
Sub OnReportStart
Set oDict = CreateObject("Scripting.Dictionary") Set rsQuery = rpt.DataSource_Shelby_.RecordSet
If rpt.DataSource_Shelby_.RecordSet.RecordCount > 0 Then
intCounter = 1 strCurrItem = "" strTestItem = "" strColumnName = "QueryColumnName" strPromptMessage = "Enter the prompt message here." strPromptTitle = "Enter the Prompt Title here"
Do strCurrItem = rsQuery(strColumnName) If strCurrItem <> strTestItem Then oDict.Add intCounter, strCurrItem strList = strList & vbCR & intCounter & " - " & strCurrItem strTestItem = strCurrItem intCounter = intCounter + 1 End If rsQuery.MoveNext Loop Until rsQuery.EOF
Do intChoice = InputBox(strPromptMessage & vbCr & strList, strPromptTitle) If intChoice <> "" and IsNumeric(intChoice) Then strItemChoice = oDict.Item(CInt(intChoice)) rsQuery.Filter = strColumnName & " = '" & strItemChoice & "'" If rsQuery.RecordCount > 0 Then Exit Do Else rsQuery.Filter = "" End If If intChoice = "" Then Exit Do Loop
rsQuery.MoveFirst
Else
MsgBox("There are no results to use for this report. Please check the query and try again after you have a set of results.")
End If
End Sub
Thursday, September 10, 2009
Padding Numbers with Leading Zeroes
select
CoNu, NameCounterfrom
Shelby.CNHst
select
CoNu = cast(CoNu as varchar(4)), NameCounter = cast(NameCounter as varchar(7))from
Shelby.CNHst
select
CoNu = '000' + cast(CoNu as varchar(4)), NameCounter = '000000' + cast(NameCounter as varchar(7))from
Shelby.CNHst
select
CoNu = right('000' + cast(CoNu as varchar(4)), 4), NameCounter = right('000000' + cast(NameCounter as varchar(7)), 7)from
Shelby.CNHst
Tuesday, September 8, 2009
Counting Rows
select count(*) from Shelby.NANames
-- Shows all user tables and row counts for the current database
-- Remove is_ms_shipped = 0 check to include system objects
-- i.index_id < 2 indicates clustered index (1) or hash table (0)
SELECT o.name,
ddps.row_count
FROM sys.indexes AS i
INNER JOIN sys.objects AS o ON i.OBJECT_ID = o.OBJECT_ID
INNER JOIN sys.dm_db_partition_stats AS ddps ON i.OBJECT_ID = ddps.OBJECT_ID AND i.index_id = ddps.index_id
WHERE i.index_id < 2
AND o.is_ms_shipped = 0
ORDER BY o.NAME
Posting SQL Code to HTML Pages
& l t ;
(without the spaces; I had to add spaces so the code wouldn't change into the symbol in this post) instead. You might want to change the "greater than" symbol to & g t ;
(again, don't put in the spaces) while you're at it.Friday, August 28, 2009
Handling Zero Results in a VBScript-Powered Report
Sub onReportStart
If rpt.DataSource_Shelby_.RecordSet.RecordCount > 0 Then
{insert your data filtering or manipulation code here}
Else
MsgBox("There are no results to use for this report. Please check the query and try again after the query returns results.")
End If
End Sub
Wednesday, August 19, 2009
Querying the Schema
select distinct
table_name
from
information_schema.columns
where
table_schema = 'Shelby' and column_name in ('WhoUpdated', 'WhenUpdated') and table_name not like 'vw_%'
order by
table_name
Monday, August 17, 2009
Listing Husband and Wife on One Row - Part 3
/* query begins */
select
SelectedName = SelectedNames.FirstMiddle + ' ' + SelectedNames.LastName,
Spouse = Spouses.FirstMiddle + ' ' + Spouses.LastName,
CombineIfMarried =
case /* This begins a conditional tree. The top branch of the tree is to check that the selected person is married. */
when SelectedNames.MaritalStatus in ('M','R') then
case /* The next branch is to check the gender of the selected person. */
when SelectedNames.Gender = 'M' then
case /* The bottom banch determines if the person and the spouse have the same last name. If there is no spouse info in the database, both comparisons will fail and the "else" condition applies. */
when SelectedNames.DifName + Spouses.DifName = 0 then Spouses.FirstMiddle + ' & ' + SelectedNames.FirstMiddle + ' ' + SelectedNames.LastName + isnull(' ' + SelectedNamesSuffixes.Descr, '')
when SelectedNames.DifName + Spouses.DifName = -1 then SelectedNames.FirstMiddle + ' ' + SelectedNames.LastName + isnull(' ' + SelectedNamesSuffixes.Descr, '') + ' & ' + Spouses.FirstMiddle + ' ' + Spouses.LastName
else 'Mr. & Mrs. ' + SelectedNames.FirstMiddle + ' ' + SelectedNames.LastName + isnull(' ' + SelectedNamesSuffixes.Descr, '')
end
when SelectedNames.Gender = 'F' then
case
when SelectedNames.DifName + Spouses.DifName = 0 then SelectedNames.FirstMiddle + ' & ' + Spouses.FirstMiddle + ' ' + Spouses.LastName + isnull(' ' + SpousesSuffixes.Descr, '')
when SelectedNames.DifName + Spouses.DifName = -1 then Spouses.FirstMiddle + ' ' + Spouses.LastName + isnull(' ' + SpousesSuffixes.Descr, '') + ' & ' + SelectedNames.FirstMiddle + ' ' + SelectedNames.LastName
else 'Mr. & Mrs. ' + SelectedNames.LastName
end
else 'Mr. & Mrs. ' + SelectedNames.LastName
end
else SelectedNames.FirstMiddle + ' ' + SelectedNames.LastName + isnull(' ' + SelectedNamesSuffixes.Descr, '')
end
from
Shelby.NANames as SelectedNames inner join
Shelby.NAProfiles as Profiles on SelectedNames.NameCounter = Profiles.NameCounter and Profiles.Profile = 'TESTGRUP' left join
Shelby.NANames as Spouses on SelectedNames.FamNu = Spouses.FamNu and Spouses.UnitNu = case when SelectedNames.UnitNu < 2 then abs(SelectedNames.UnitNu -1) end left join
Shelby.NASuffixes as SelectedNamesSuffixes on SelectedNames.SuffixCounter = SelectedNamesSuffixes.Counter left join
Shelby.NASuffixes as SpousesSuffixes on Spouses.SuffixCounter = SpousesSuffixes.Counter/* query ends */
The CASE statement to put the names together has three levels, as noted with the comments in the statement. The first level divides those who are married from those who are not. The second level divides the people selected into male and female, and the third level divides those spouses with different last names.
Tuesday, August 4, 2009
Listing Husband and Wife on One Line - Part 2
select
HHFirstName = max(case when Names.UnitNu = 0 then Names.FirstMiddle end),
HHLastName = max(case when Names.UnitNu = 0 then Names.LastName end),
SPFirstName = max(case when Names.UnitNu = 1 then Names.FirstMiddle end),
SPLastName = max(case when Names.UnitNu = 1 then Names.LastName end)
from
Shelby.NANames as Names inner join
Shelby.NAProfiles as Profiles on Names.NameCounter = Profiles.NameCounter and Profiles.Profile like 'MUAC%'
group by
Names.FamNu
Tuesday, July 28, 2009
Listing Husband and Wife on One Line - Part 1
- NameCounter - a numeric value that is unique for every person in the table
- FamNu - a numeric value shared by all members of the same family; it is helpful to realize that this value is the same as the head of household's NameCounter value.
- UnitNu - a numeric value indicating the family position in the household; the head of house value is zero (0) and the spouse value is one (1).
select
HeadOfHouse = HH.FirstMiddle + ' ' + HH.LastName,
Spouse = SP.FirstMiddle + ' ' + SP.LastName
from
Shelby.NANames as HH left join
Shelby.NANames as SP on HH.FamNu = SP.FamNu and SP.UnitNu = 1
where
HH.UnitNu = 0 and HH.TitleCounter > -1
select
HeadOfHouse = ChosenNames.FirstMiddle + ' ' + ChosenNames.LastName,
Spouse = Spouses.FirstMiddle + ' ' + Spouses.LastName
from
Shelby.NANames as ChosenNames left join
Shelby.NANames as Spouses on ChosenNames.FamNu = Spouses.FamNu and Spouses.UnitNu = ABS(ChosenNames.UnitNu - 1)
where
any conditional statements
Tuesday, July 21, 2009
Existential Queries
select NameCounter, FirstMiddle, LastNamefrom Shelby.NANameswhere exists (select * from Shelby.NAProfiles where Shelby.NAProfiles.Profile = 'value1' and Shelby.NAProfiles.NameCounter = Shelby.NANames.NameCounter) and not exists (select * from Shelby.NAProfiles where Shelby.NAProfiles.Profile = 'value2' and Shelby.NAProfiles.NameCounter = Shelby.NANames.NameCounter)
Monday, July 13, 2009
Comparing Dates in SQL
The problem is that dates are not stored merely as dates but as datetime values. This means there is an hour, minute, second, and millisecond value associated with every date, even if that value is ignored by the user interface. In most cases this is not going to cause a mistake when comparing two different dates, but it does cause a problem when one is trying to find equivalence between two instances of the same date.
Here is the underlying "gotcha": When the time of day does not matter, most databases store dates as if they were at midnight of the day in question whereas the GETDATE( ) function returns the date and current time. Any calculations based on the GETDATE( ) value also have the current time. And because 1/1/2009 00:00:00 is technically less than 1/1/2009 15:39:15 (or any other time after midnight), those two values will not satisfy a comparison using "=" even though they signify the same calendar day.
Here is a simple CASE statement showing the use of DATEDIFF( ) in conjunction =, >, or <.
CASE
WHEN DATEDIFF(day, date1, date2) = 0 THEN "The dates are the same."
WHEN DATEDIFF(day, date1, date2) > 0 THEN "Date1 is earlier than Date2."
WHEN DATEDIFF(day, date1, date2) <>
Using an approach like this will always give you the results you want, without any problems from the time element of the datetime value.
Monday, July 6, 2009
Reporting Services and check boxes
Interestingly, even though SQL Server Reporting Services is much superior to Active Reports in many respects, it is lacking in a "checkbox" object. This seems to me to be a significant oversight, but it not completely insurmountable.
Although there is no "checkbox" object in Reporting Services, you can emulate one using a TextBox, if you are willing to have an "X" instead of an actual check mark in the box. Just do the following.
Create a new text box, put an "X" in it, and change the following properties to the listed values:
- BorderStyle: Solid
- Color: = IIf (Fields!MyField.Value, "Black", "White")
- Size: .175in, .175in (this fits a 10pt font; make it larger for a bigger font)