Get the Names of Stored Procedures that Reference a Given Table in SQL Server

SQL Server logo

One of my colleagues sent me a very handy bit of code this morning that I thought would be helpful to post.

Below are two options which, when run against the database, should return the names of all stored procedures that reference the named table.

----Option 1
SELECT DISTINCT so.name
FROM syscomments sc
INNER JOIN sysobjects so ON sc.id=so.id
WHERE sc.TEXT LIKE '%tablename%'

----Option 2
SELECT DISTINCT o.name, o.xtype
FROM syscomments c
INNER JOIN sysobjects o ON c.id=o.id
WHERE c.TEXT LIKE '%tablename%'

I am told that the functionality of the first option has been verified.

Sending SQL Server Stored Procedure Results as an HTML Table in Email Using Python

Python logo

By modifying the code I used for sending email attachments, I am able to execute a SQL Server stored proc and format the results as an HTML table in an email. Also, I use the sys.exit command if there are no rows returned so that the email is not sent if there is no data returned.

# disabled username / password logon for use in our Exchange environment

######### Setup your stuff here #######################################

host = 'smtp.whatever.com' # specify port, if required, using a colon and port number following the hostname

fromaddr = '[email protected]' # must be a vaild 'from' address in your environment
toaddr  = ['[email protected]'] # list of email addresses
ccaddr  = [''] # list of email addresses
bccaddr  = ['[email protected]'] # list of email addresses
replyto = fromaddr # unless you want a different reply-to

# username = 'username' # not used in our Exchange environment
# password = 'password' # not used in our Exchange environment

msgsubject = 'Email Subject'
htmlmsgtext = "<h2>Report for "  # text with appropriate HTML tags
connstring = 'DRIVER={SQL Server};SERVER=servername;DATABASE=dbname;UID=userid;PWD=password'

######### In normal use nothing changes below this line ###############

import smtplib, pyodbc, sys
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE
from HTMLParser import HTMLParser
import datetime as dt
yesterday = dt.datetime.now() - dt.timedelta(days=1)

date = "'" + yesterday.strftime('%m-%d-%Y') + "'"
htmlmsgtext = htmlmsgtext + date + "</h2>"
conn=pyodbc.connect(connstring)
cursor=conn.cursor()
cursor.execute("exec dbo.sp_RandomStoredProc " + date)
rows = cursor.fetchall()
column_names = [d[0] for d in cursor.description]
cursor.close()
del cursor
if len(rows) == 0:
    sys.exit
htmlmsgtext = htmlmsgtext + '<table style="border:2px solid black">n'
htmlmsgtext = htmlmsgtext + '<tr>n'
for column_name in column_names:
    htmlmsgtext = htmlmsgtext + '<th style="border:1px solid black; text-align:center">' + column_name + '</th>'
htmlmsgtext = htmlmsgtext + 'n</tr>n'
for row in rows:
    for column in row:
        htmlmsgtext = htmlmsgtext + '<td style="border:1px solid black; text-align:center">' + str(column) + '</td>'
    htmlmsgtext = htmlmsgtext + 'n</tr>n'
htmlmsgtext = htmlmsgtext + '</table>n'

# A snippet - class to strip HTML tags for the text version of the email

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()

########################################################################

try:
    # Make text version from HTML - First convert tags that produce a line break to carriage returns
    msgtext = htmlmsgtext.replace('</br>',"r").replace('
',"r").replace('
',"r")
    # Then strip all the other tags out
    msgtext = strip_tags(msgtext)

    # necessary mimey stuff
    msg = MIMEMultipart()
    msg.preamble = 'This is a multi-part message in MIME format.n'
    msg.epilogue = ''

    body = MIMEMultipart('alternative')
    body.attach(MIMEText(msgtext))
    body.attach(MIMEText(htmlmsgtext, 'html'))
    msg.attach(body)   

    msg['From'] = fromaddr
    msg['To'] = COMMASPACE.join(toaddr)
    msg['CC'] = COMMASPACE.join(ccaddr)
    msg['Subject'] = msgsubject
    msg['Reply-To'] = replyto

    print 'To addresses follow:'
    print toaddr

    # The actual email sendy bits
    server = smtplib.SMTP(host)
    server.set_debuglevel(False) # set to True for verbose output
    recipients = toaddr + ccaddr + bccaddr
    # Comment this block and uncomment the below try/except block if TLS or user/pass is required.
    server.sendmail(fromaddr, recipients, msg.as_string())
    print 'Email sent.'
    server.quit() # bye bye

    # try:
        # # If TLS is used
        # server.starttls()
        # server.login(username,password)
        # server.sendmail(fromaddr, recipients, msg.as_string())
        # print 'Email sent.'
        # server.quit() # bye bye
    # except:
        # # if tls is set for non-tls servers you would have raised an exception, so....
        # server.login(username,password)
        # server.sendmail(fromaddr, recipients, msg.as_string())
        # print 'Email sent.'
        # server.quit() # bye bye        

except:
    print "Email NOT sent to %s successfully. ERR: %s %s %s " % (str(toaddr), str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]) )
    #just in case

Troubleshooting an SSRS Error

SSRS logo

SQL Server Reporting Services is a great tool that comes with SQL Server, and it is the tool I prefer to use when a report is needed.

I have built and continue to maintain many such reports, and once the reports are built, they don’t often need updating, unless the database schema changes or the customer’s requirements change.  As a result, once they’re deployed, the reports don’t often require a lot of minding.

However, just as with any application, unforeseen events can cause the reports to fail.  In my case today, bad data was allowed to be inserted into the database that was the source for one of my reports.  The mechanism that inserted the data allowed the data in, but the code used to built the result set for the report threw an exception when it encountered the data.  For security reasons, that exception was caught and a generic error appeared in the Web browser when attempting to run the report:

  • An error has occurred during report processing.
    • Query execution failed for data set <>
      • For more information about this error navigate to the report server on the local server machine, or enable remote errors

To find out what the source of the error was, I opened the report in the BI Development Server from SQL Server 2005.  The data source was a stored procedure.  When executing the SP in Query Analyzer, I got this error:

“Msg 8115, Level 16, State 6, Line 49
Arithmetic overflow error converting float to data type numeric.”

My next step was to get the source for the SP, commenting out the CREATE statement, and declaring and setting the parameters as variables to change the SP source code into ad hoc executable statements.  By plugging the dates in question into these variables, I got the same error as above.

The line causing problem in the stored proc updated a table variable based on values from a static table.  Since I could not directly determine what values in the static table were causing the SP to throw the error, I read in all of the values that were in the column of the table variable that were normally joined to the static table into a cursor.

I then scrolled through the cursor, using SELECT statements that contained a modified version of the expression that had been in the UPDATE statement, until I found out which value caused the problem.  Also, I added two SELECT identical statements to the cursor, one before and one after the SELECT statement that had been modified from the original UPDATE statement.  These two SELECT statements simply read “SELECT <<cursor variable>>” to indicate which value was being used for each iteration through the cursor.

When the value causing the problem was reached, the error statement appeared in the Messages pane of the Query Analyzer between two lines, each of these lines showing the value that caused the error.  By then using that value in the WHERE clause of a SELECT statement against the static table, I was able to find out what bad data had been entered.