Archive for SQLServer

ALTER LOGIN after Windows user or group name has been changed

If a Windows AD group or user has been renamed, and if that group or user was granted access to SQL Server in the past, then you can use ALTER LOGIN to rename the login inside of SQL Server:

ALTER LOGIN [myDomain\oldName] WITH NAME = [myDomain\newName]

It is not necessary to adjust user names in SQL Server databases that this login has access to, but you may want to do it just for consistency. Here is the command to do that:

ALTER USER [myDomain\oldName] WITH NAME = [myDomain\newName]

Note that renaming an AD user or group does not change its SID. You can check an AD user or group’s SID with psgetsid, part of the very handy Sysinternal tool suite.

Comments

Generating dimension data for dates

Most analytical and BI databases have date dimension table(s). One frequently needs to generate and populate such data. I present a solution below for such data generation, written in Python. Please use different database drivers/modules to connect to your specific database server (MySQL, SQL Server, Oracle, etc.) for data population.

Notes:

1. It takes 2 parameters, start date and end date, in YYYYMMDD format, inclusive. Extensive error checking is built in, but let me know if you have comments/suggestions;

2. The script produce a Python dictionary (associated array) and print out its content;

3. The output includes dayNumber: a day’s position in a year. For example, 2011-02-01 is the 32ed day in 2011, therefore its dayNumber is 32;

4. The output includes weekNumber: a week’s position in a year. The week number in year is based on ISO standard. From documentation: the ISO year consists of 52 or 53 full weeks, where a week starts on a Monday and ends on a Sunday. The first week of an ISO year is the first (Gregorian) calendar week of a year containing a Thursday. This is called week number 1, and the ISO year of that Thursday is the same as its Gregorian year.

So, 2011-01-01 has the weekNumber 52, because it falls on a Saturday and belongs to the last week of 2010.

5. The output includes weekday information as well. 4 different variations are included:
Sunday 0, Monday 1, and so on
Sunday 1, Monday 2, and so on
Monday 0, Tuesday 1, and so on
Monday 1, Tuesday 2, and so on

6. The script requires the argparse module. It comes with Python 2.7. Python version prior to 2.7 does not have it by default, therefore you need to install it.

import argparse, sys, time
from datetime import date, timedelta
parser = argparse.ArgumentParser(description="Generating date dimension data")
parser.add_argument('-s', '--startDate', help='Start date in YYYYMMDD format', required=True, dest='startDate')
parser.add_argument('-e', '--endDate', help='end date in YYYYMMDD format', required=True, dest='endDate')
argList = parser.parse_args()
if (((not argList.startDate.isdigit()) or (not (len(argList.startDate) == 8))) or ((not argList.endDate.isdigit()) or (not (len(argList.endDate) == 8))) or (argList.startDate > argList.endDate)):
	print "Input(s) must be numeric in YYYYMMDD format and end date must not be earlier than start date"
	sys.exit (1)
try:
	startDate = date(int(argList.startDate[0:4]), int(argList.startDate[4:6]), int(argList.startDate[6:8]))
	endDate = date(int(argList.endDate[0:4]), int(argList.endDate[4:6]), int(argList.endDate[6:8]))
except ValueError:
	print "Input(s) must be valid date value in YYYYMMDD format"
	sys.exit (1)
start = time.time()
while startDate <= endDate:
	dateInfo = {'dateYYYYMMDD': startDate.strftime('%Y%m%d'), 'calDate': startDate.strftime('%Y-%m-%d'), 'calDay': startDate.day, 'calMonth': startDate.month, 'calYear': startDate.year}
	dateInfo['dayOfWeekSunday0Monday1'] = startDate.isoweekday() % 7
	dateInfo['dayOfWeekSunday1Monday2'] = startDate.isoweekday() % 7 + 1
	dateInfo['dayOfWeekSunday6Monday0'] = startDate.weekday()
	dateInfo['dayOfWeekSunday7Monday1'] = startDate.isoweekday()
	dateInfo['dayNumber'] = startDate.toordinal() - date(startDate.year - 1, 12, 31).toordinal()
	dateInfo['weekNumber'] = startDate.isocalendar()[1]
	print dateInfo
	startDate = startDate + timedelta(1)

Comments (1)

Sysinternals and PAL

Sysinternals and PAL (Performance Analysis of Logs) are two fantastic tools for general server information gathering and troubleshooting on Windows.

Sysinternals suite is a set of tools that can be downloaded freely from Microsoft. One thing that is particularly attractive about them is that they can be run directly after downloading without special installation and all the footprints a typical installation leaves on the host machine (new directories under C:\Program Files\, registry entries, data files and what have you). I found them very valuable and handy.

In particular, psInfo provides good summary information of the server. For example, psinfo -s -h -d provides basic information about the system, software installed, Windows hot fixes installed, and disk volume information.

PAL: install PAL on your test/analysis/general purpose machine. Install the mschart control as it is a prerequisite of PAL. Here is how I used it:

1. Produce Perfmon data gathering template files using PAL. I exported 3 template files: overview, quick overview, and SQL Server 2005/2008;

Perfmon is the general-purpose data instrumentation tool on Windows. Through Perfmon you can gather system wide counters for things like CPU, memory, network, and disk IO. In addition, a lot of applications such as SQL Server, Exchange, and others, expose application level Instrumentation data such that you can collect them via Perfmon as well.

It is best to have a few handy data collection template, hence this step.

2. On the Windows server that I am interested in monitoring, import Perfmon counter template file produced above by opening a DOS prompt under Administrator and executing:

logman import -n templateNameIdefine -xml pathAndName2TemplateXmlFile

3. Open Perfmon, find the one you imported, and start collecting

4. After collection is done, copy the log file and use PAL for analysis. It will generate a very nice and intuitive report. Please don’t run PAL on the system you are diagnosing. Run it somewhere else. Be patient, as it will take a while for PAL to churn through the data (it took 2 hours on a Rackspace cloud server with 2 CPUs and 1 gig of RAM for a file about 30 meg)

Comments

Some SQL Server 2008 page compression observations

A few days ago I wrote about Infobright’s column-based storage engine, and compared the sizes of raw text data file, gzipped file, MyISAM files, and Infobright files. At that time, I also wanted to compare that against data compression in SQL Server 2008, which is a new feature. But the Windows cloud server instance I fired up at the time didn’t have enough disk space, so I temporarily aborted that endeavour, until today.

Once again, testing data was generated using TPC-H’s dbgen tool. In fact I took the same steps outlined here. The total raw text file size is around 8.8 GB. I then created 2 SQL Server tables without any index (heap), one without compression and one with page compression. The DDL used is based on DDL listed in this post, without the indexes.

I used BULK INSERT for data loading. Here is the statement used:

BULK INSERT [testDb].[dbo].[LINEITEM]
FROM 'C:\Users\Administrator\Documents\lineitem.tbl'
WITH(CHECK_CONSTRAINTS,CODEPAGE='RAW',DATAFILETYPE='char',
FIELDTERMINATOR='|',ROWTERMINATOR='0x0a')

I then calculated storage space taken with sp_spaceused stored procedure.

Here is the results in GB:

Raw Text: 8.8
Raw Text After GZIP: 2.6
Uncompressed SQL Server table data size: 9.5
Compressed SQL Server table data size: 7.4

For clarity’s sake, here is the results with Infobright and MyISAM on Linux:
MyISAM File Size: 7.2
InfobrightFileSize: 1.5

SQL Server 2008 provides a system stored procedure that gives a size estimation if compression is used for a non-compressed table/index.The estimated size of a compressed table is 5.4 GB. Comparing it with 7.4, the estimation appears to be optimistic, in this case.

Comments

Data generation with TPC-H’s dbgen for load testing

2011-06-26 update:

I am not sure if there are any changes in the latest make and gcc packages. Anyway, I noticed when run make, I encountered the message below:

make: g: Command not found
make: [qgen] Error 127 (ignored)

To fix this, find where gcc is at, then created a symbolic link g that points to gcc. All is well afterwards:
[root@ip-10-245-209-196 dbgen]# which gcc
/usr/bin/gcc
[root@ip-10-245-209-196 dbgen]# cd /usr/bin/
[root@ip-10-245-209-196 bin]# ln -s gcc g

End update
Recently I found myself doing some data loading benchmark testing with table partition. Data loading and storing for BI/DW/DSS stuff almost always involves data partitioning. SQL Server partition has a nice feature called partition switch, where you can swap data in and out of a partitioned table. In MySQL, it is called exchange, but it hasn’t been released. Perhaps other RDBMS have similar things as well.

To facilitate testing, I need some data that lends itself easily for partition, which led me to TPC-H’s dbgen tool.

dbgen is pretty easy to build and compile on Linux. It also has a Windows version. I didn’t bother trying because getting it built on Linux was such a quick and easy process. However, the documentation of dbgen, if it can be called as such, leaves a lot to be desired. But by experimenting and surfing around, I was able to get what I needed.

Instructions here are for CentOS. Building it on other flavors of Linux should also be easy.

1. yum install make and yum install gcc if you don’t have that already;

2. wget source code;

3. tar xf tarball;

4. cd dbgen

5. vi makefile.suite and change 4 lines. See uncommented lines below:

################
## CHANGE NAME OF ANSI COMPILER HERE
################
CC      = gcc
# Current values for DATABASE are: INFORMIX, DB2, TDAT (Teradata)
#                                  SQLSERVER, SYBASE, ORACLE
# Current values for MACHINE are:  ATT, DOS, HP, IBM, ICL, MVS,
#                                  SGI, SUN, U2200, VMS, LINUX, WIN32
# Current values for WORKLOAD are:  TPCH
DATABASE= SQLSERVER
MACHINE = LINUX
WORKLOAD = TPCH

Note: It does not have MySQL and others for DATABASE setting, but that really doesn’t matter, because the file generated, which is delimited by a pipe, |, can be used for loading into any system.

6. make -f makefile.suite
This creats the dbgen binary that we can use in the next step.

7. ./dbgen -h gives you a brief description of switches that can be used. I am just interested in creating the lineitem table, because it has a good combination of integer, decimal, date, and character fields. The date inside the tables spans between 1992-01-01 and 1998-12-31, which is good for a partitioned table based on date. The DDL for this table is inside the dss.ddl file in the same directory. Please modify it as necessary. In fact, Lubor Kollar has it ready for SQL Server here, and Vadim Tkachenko has it for MySQL here.

Here is the command I used and its results:

[root@centos dbgen]# ./dbgen -v -T L -s 2
TPC-H Population Generator (Version 2.14.0)
Copyright Transaction Processing Performance Council 1994 - 2010
Generating data for lineitem table/
Preloading text ... 100%
Do you want to overwrite ./lineitem.tbl ? [Y/N]: Y
done.

-v: verbose, -T L: lineitem only, -s 2: scale factor of 2. My understanding is that it roughly indicates that the end file will be close to 2 gig.

It turns out the end file is about 1.5 gig, with close to 12 million rows in it.

-rw-r--r-- 1 root root  1.5G Mar 30 21:33 lineitem.tbl
[root@centos dbgen]# wc -l lineitem.tbl
11997996 lineitem.tbl

You can play around with different key switches to get the size of file you want. For example, quoting from Lubor Kollar:

dbgen –T L -s 4 -C 3 -S 1

Using the –s option, we set the scale to 4 that generates a Lineitem table of 3GB. Using the –C option we split the table into 3 portions, and then using the –S option we chose only the first 1GB portion of the Lineitem table.

Running the command above, I got a file named lineitem.tbl.1 with close to 8 million rows, about 976 meg.

When trying to load it into SQL Server with SSIS bulk copying task, remember the delimiter is | and line separator is LF (line feed). For MySQL, something like this should do:

load data local infile '/root/dbgen/lineitem.tbl.1' into table lineitem fields terminated by '|' lines terminated by '\n';

Comments (2)

SSDs and their impact on database servers

Vadim Tkachenko published interesting benchmark results with PCI-E based SSDs here. I recently got a chance to benchmark FusionIO’s 320 GB PCI-E drive. It was really impressive. My results, done on Windows with sqlio, are consistent (not identical, of course, but in the same ballpark) with what Vadim reported in that blog post, done with sysbench on Linux.

sqlio is a popular IO throughput testing tool from Microsoft. I didn’t get to test the throughput when the SSD is close to full. The key takeaways that I learned from my testing are:

1. I can confirm that there is no difference between random and sequential IO, contrary to the traditional spindle based hard disks;

2. Read is significantly faster than write. Reads and writes with 64 threads can achieve around 1.4 GB/S and 400 MB/S throughput, respectively.

It is good to hear that another vendor, Virident, also offers similar PCI-E based SSDs around similar price points. I think as the technology behind SSDs matures and price of it decreases, its impact on the database field could be significant.

Most, if not all, database platforms have put a lot of emphasis on achieving sequential reads and minimizing fragmentation. This is especially true for business intelligence databases. However, as SSDs gradually take over high end storage market for mission critical databases, with no difference between random and sequential access on those disks, it could relegate our obsession on sequential reads and fragmentation to the back burner, which is significant, in my opinion.

I am not suggesting that SSDs effectively make the fragmentation issue disappear, mind you. Because many database software, be it MySQL (perhaps the InnoDB storage engine in particular), SQL Server, or Oracle, uses prefetch, read-ahead read, or whatever the lingo maybe, to anticipate the need and bring additional data into cache in advance in case they are used next. This can be especially effective for business intelligence applications. In the case of prefectching, fragmentation is still undesirable, but the speed of SSDs could make it a less pressing issue than it has been, which is significant, I think.

A few years back, when multi-core servers started emerging, database software vendors invested time and energy to take advantage of that, with new features added and white paper created for NUMA architecture. I think it will be interesting to watch how emerging SSDs in the server market will be exploited. Percona’s white paper, Scaling MySQL Deployments With Percona Server and Virident tachIOn Drives, that talks “scaling up” with SSDs, instead of “scaling out” with sharding, is a pretty interesting idea.

Comments

Windows disk throughput benchmark steps with sqlio

Credits to Linchi Shea, SQLServerPedia/Brent Ozar, and Jonathan Kehayias for their discussions on sqlio.

Update: The test below, against one drive, can run for more than 1 hour. That is a long time. I’ve tested running 2 sqlio batch scripts against 2 locally attached drives, similar to the ones listed below, and found the results to be consistent with running those 2 concurrently.

1. Download and install sqlio;
2. Modify param.txt in the same directory where sqlio is. Create a testing file that is 24 gig, or sufficiently large to beat hardware cache along the way. For example, to benchmark drive m, the entry should look like this:

m:\testfile.dat 2 0x0 20480

If you use mount point, it should look like this:

m:\mountPointDirectoryName\testfile.dat 2 0x0 20480

3. Make sure file in step 2 is saved. In DOS command line, under the sqlio installation directory, run the command below. It will create a file for use later:

sqlio -kW -s10 -fsequential -o8 -b8 -LS -Fparam.txt timeout /T 10

4. Create a batch file with the following line as content, let’s call it mDrive.bat. SQLServerPedia article lets each test run 120 seconds, 2 minutes. In my testing against local disks, I made it 30 seconds. Actually I ran both 120 and 30 and didn’t see much of a difference. So I will use 30 seconds.

Note: to test against Windows mount point, the lines below should be like this:

sqlio -kW -t2 -s30 -dM -o1 -frandom -b64 -BH -LS \mountPointDirectoryName\Testfile.dat
sqlio -kW -t2 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o4 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o8 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o16 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o32 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o64 -frandom -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o128 -frandom -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t2 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t4 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t8 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t16 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t32 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kW -t64 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t2 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t4 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t8 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t16 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t32 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o1 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o2 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o4 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o8 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o16 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o32 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o64 -fsequential -b64 -BH -LS Testfile.dat
sqlio -kR -t64 -s30 -dM -o128 -fsequential -b64 -BH -LS Testfile.dat

5. In DOS command line, under the sqlio installation directory, run the command below. It takes more than 1 hour, but less than 2 hours to finish:

mDrive.bat > mDriveSqlioResults.txt

6. Run this Python script to parse mDriveSqlioResults.txt into a CSV file;

7. Open the CSV file with Excel, create a new sheet, insert a column chart. Drag the chart to make it decent size. Right click on “Select Data…” or something like that, then go back to the first sheet to pick 2 columns of data for this chart. I found charts with MBPS, IOPS, avgLatencyInMS, and maxLatencyInMS are helpful.

Comments

Parse sqlio log file with Python pyparsing

A few weeks ago I posted some questions on 3 Python text processing modules: pyparsing, SimpleParse, and NLTK. Today I need to analyze a log file generated by sqlio. I decided to use pyparsing. I am pretty pleased with it.

I am aware that there are at least 2 utility scripts for this: one is in Perl and then other is in Windows PowerShell. But I wanted to write in Python.

Noteworthy points:

1. In analyzing and building up the grammar/structure of the text for Python, I opted for line by line analysis and coding. I think this brings clarity and is easier to read. It felt a bit tedious in building it up, but once it is done, it is pretty rewarding to see the end results;
2. After parsing, the script writes the results to a csv file. You can then open that in a spreadsheet program to create some charts. Seeing the results in a column diagram chart brings clarity and focus to the result. It worked pretty well for me.

All in all, a good and productive day. Below are the details. The top part is the sample log content to be parsed, followed by the program.

"""
c:\Program Files (x86)\SQLIO>sqlio -kW -t2 -s30 -dH -o1 -frandom -b64 -BH -LS Testfile.dat
sqlio v1.5.SG
using system counter for latency timings, 2208037 counts per second
2 threads writing for 30 secs to file H:Testfile.dat
	using 64KB random IOs
	enabling multiple I/Os per thread with 1 outstanding
	buffering set to use hardware disk cache (but not file cache)
using current size: 24576 MB for file: H:Testfile.dat
initialization done
CUMULATIVE DATA:
throughput metrics:
IOs/sec:  6106.50
MBs/sec:   381.65
latency metrics:
Min_Latency(ms): 0
Avg_Latency(ms): 0
Max_Latency(ms): 5
histogram:
ms: 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24+
%: 99  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
c:\Program Files (x86)\SQLIO>sqlio -kW -t2 -s30 -dH -o2 -frandom -b64 -BH -LS Testfile.dat
sqlio v1.5.SG
using system counter for latency timings, 2208037 counts per second
2 threads writing for 30 secs to file H:Testfile.dat
	using 64KB random IOs
	enabling multiple I/Os per thread with 2 outstanding
	buffering set to use hardware disk cache (but not file cache)
using current size: 24576 MB for file: H:Testfile.dat
initialization done
CUMULATIVE DATA:
throughput metrics:
IOs/sec:  6951.19
MBs/sec:   434.44
latency metrics:
Min_Latency(ms): 0
Avg_Latency(ms): 0
Max_Latency(ms): 6
histogram:
ms: 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24+
%: 87 12  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
"""
from pyparsing import *
output = open("sample.csv", "w")
input = open("hDriveSqlioResults.txt", "r")
data = input.read()
#------------------------------------------------------------------------
# Define Grammars
#------------------------------------------------------------------------
integer = Word(nums)
singleLetter = Word(alphas, max=1)
realNumber = Combine(Word(nums) + '.' + Word(nums))
reading = Literal("reading")
writing = Literal("writing")
readingOrWriting = reading | writing
random = Literal("random")
sequential = Literal("sequential")
randomOrSequential = random | sequential
#Line 1 is useful. We want to suck out the juicy parts.
sectionBeginning = CaselessLiteral("c:\\program files (x86)\\sqlio>sqlio ")
ioKind = "-k" + singleLetter("k")
numThreads = "-t" + integer("t")
numSeconds = "-s" + integer("s")
driveOrMountPath = "-d" + singleLetter("d")
outstandingRequests = "-o" + integer("o")
accessType = "-f" + Word(alphas)("f")
ioBlockSizeInKB = "-b" + integer("b")
#Line 2 can be safely ignored
line2 = "sqlio v" + restOfLine
#Line 3 can be safely ignored
line3 = "using system counter for latency timings" + restOfLine
#Line 4 can be safely ignored
line4 = integer + "threads" + readingOrWriting + restOfLine
#Line 5 can be safely ignored
line5 = "using" + integer + "KB" + randomOrSequential + "IOs" + restOfLine
#Line 6 can be safely ignored
line6 = "enabling multiple " + restOfLine
#Line 7 can be safely ignored
line7 = "buffering set to use" + restOfLine
#Line 8 we should get the file size out
line8 = "using current size: " + integer("fileSize") + Word(alphas)("fileSizeUnit") + restOfLine
#Line 9 can be safely ignored
line9 = "initialization done" + restOfLine
#Line 10 can be safely ignored
line10 = "CUMULATIVE" + restOfLine
#Line 11 can be safely ignored
line11 = "throughput" + restOfLine
#Line 12 we want IOPS
line12 = "IOs/sec:" + realNumber("IOPS") + restOfLine
#Line 13 we want MBPS
line13 = "MBs/sec:" + realNumber("MBPS") + restOfLine
#Line 14 can be safely ignored
line14 = "latency" + restOfLine
#Line 15 we need to get minLatency out
line15 = "Min_Latency(ms):" + integer("minLatency") + restOfLine
#Line 16 we need to get avgLatency out
line16 = "Avg_Latency(ms):" + integer("avgLatency") + restOfLine
#Line 17 we need to get maxLatency out
line17 = "Max_Latency(ms):" + integer("maxLatency") + restOfLine
#Line 18 can be safely ignored
line18 = "histogram" + restOfLine
#Line 19 can be safely ignored
line19 = "ms:" + restOfLine
#Line 20 can be safely ignored
line20 = "\%:" + restOfLine
extraStuff = ZeroOrMore(line14 | line15 | line16 | line17 | line18 | line19 | line20)
logEntry = sectionBeginning + ioKind + numThreads + numSeconds + driveOrMountPath + outstandingRequests + accessType + ioBlockSizeInKB + restOfLine + line2 + line3 + line4 + line5 + line6 + line7 + line8 + line9 + line10 + line11 + line12 + line13 + extraStuff
output.write("IO property, IOPS, MBPS, minLatencyInMS, avgLatencyInMS, maxLatencyInMS\n")
for tokens in logEntry.searchString(data):
	output.write("%(k)s%(t)s threads %(o)s queue %(f)s in %(b)s KB chunks,%(IOPS)s,%(MBPS)s,%(minLatency)s,%(avgLatency)s,%(maxLatency)s\n" % tokens)

Comments (5)

Utility script for instance level objects comparison between 2 SQL Server instances

Useful for SQL Server upgrade and migration, instance consistency check in the enterprise, et cetera.

Known assumptions:
1. pyodbc and proper SQL Server database driver setup and tested;
2. Windows Active Directory trusted connection to both instances. Login/password can also be used with very simple modification of the code;
3. Proper permission is set on both instances.

Known limitations:
1. It is based on object name comparison. In many cases, this is “good enough”;
2. It does not compare End Points or Server level triggers, although that should be added easily.

Command line usage:
scriptName -s sourceInstance -t targetInstance

import pyodbc, argparse
def getAllRows(cn, sql):
	cursor = cn.cursor()
	cursor.execute(sql)
	return cursor.fetchall()
def rowCompare(objectType, rowsSource, rowsTarget):
	if rowsSource == rowsTarget:
		print "%s(s) appeared to have been migrated." % objectType
	else:
		print "%s(s) not migrated yet:" % objectType
		for row in rowsSource:
			if row not in rowsTarget:
				print row[0]
parser = argparse.ArgumentParser(description='SQL Server migration/upgrade comparison: databases')
parser.add_argument('-s', '--source-server', help='Source server/instance',  required=True, dest='sInstance')
parser.add_argument('-t', '--target-server', help='Target server/instance', required=True, dest='dInstance')
argList = parser.parse_args()
try:
	cnSource = pyodbc.connect("DRIVER={SQL Server};SERVER=%s;DATABASE=master;Trusted_Connection=yes" % argList.sInstance)
	cnTarget = pyodbc.connect("DRIVER={SQL Server};SERVER=%s;DATABASE=master;Trusted_Connection=yes" % argList.dInstance)
except:
	print "Couldn't connect to %s. It is down or you might have had a typo."
"""1. Compare if all databases on the source server are present at the target server"""
print "\n"
sql = "select name from master.sys.databases order by name"
rowsSource = getAllRows(cnSource, sql)
rowsTarget = getAllRows(cnTarget, sql)
rowCompare('Database', rowsSource, rowsTarget)
"""2. Compare if all logins on the source server are present at the target server"""
print "\n"
sql = "select name from master.dbo.syslogins where name not like '##%' order by name"
rowsSource = getAllRows(cnSource, sql)
rowsTarget = getAllRows(cnTarget, sql)
rowCompare('Login', rowsSource, rowsTarget)
"""3. Compare if all linked servers on the source server are present at the target server"""
print "\n"
sql = "select name from master.sys.servers where server_id > 0 order by name"
rowsSource = getAllRows(cnSource, sql)
rowsTarget = getAllRows(cnTarget, sql)
rowCompare('Linked server', rowsSource, rowsTarget)
"""4. Compare if all jobs on the source server are present at the target server"""
print "\n"
sql = "select name from msdb.dbo.sysjobs order by name"
rowsSource = getAllRows(cnSource, sql)
rowsTarget = getAllRows(cnTarget, sql)
rowCompare('Job', rowsSource, rowsTarget)
"""5. Compare if all credentials on the source server are present at the target server"""
print "\n"
sql = "select name from master.sys.credentials order by name"
rowsSource = getAllRows(cnSource, sql)
rowsTarget = getAllRows(cnTarget, sql)
rowCompare('Credential', rowsSource, rowsTarget)
"""6. Compare if all system stored procedures inside master that start with sp_ on the source server are present at the target server"""
print "\n"
sql = "select name from master.sys.all_objects where type = 'p' and name like 'sp_%' and is_ms_shipped = 0 order by name"
rowsSource = getAllRows(cnSource, sql)
rowsTarget = getAllRows(cnTarget, sql)
rowCompare('System stored procedure', rowsSource, rowsTarget)

Comments

Search objects in SQL Server databases with pyodbc

Command line utility to search for objects based on names. Search results will be displayed in well formatted tabular format, left-justified.

1. pyodbc installed;
2. Use trusted authentication by default. Find the relevant code to adjust to login/password as needed;
3. -w for wild card search. Default is exact name match, case insensitive in almost all cases, depending on your SQL Server configuration;
4. If -d is not specified, all databases in instance will be searched, minus the ones that one not accessible at the moment. To specify database(s) you want to search into, use -d followed by database names. Separate them with comma if more than one;
5. Use -S to specify instance;
6. Good sample code for command line parameters processing and pyodbc usage;
7. Won’t work with SQL Server 2000 or lower.

Example 1: python objectSearch.py myobject -S instance1
Result: Database objects that are named myobject in instance1 will be displayed

Example 2: python objectSearch.py -S server1 -w -d db1,db2 myobject
Result: Database objects that has “myobject” in db1 and db2 in server1 will be displayed

import pyodbc, argparse
def pp(cursor, data=None, check_row_lengths=True):
    if not data:
        data = cursor.fetchall( )
    names = [  ]
    lengths = [  ]
    rules = [  ]
    for col, field_description in enumerate(cursor.description):
        field_name = field_description[0]
        names.append(field_name)
        field_length = field_description[2] or 12
        field_length = max(field_length, len(field_name))
        if check_row_lengths:
            # double-check field length, if it's unreliable
            data_length = max([ len(str(row[col])) for row in data ])
            field_length = max(field_length, data_length)
        lengths.append(field_length)
        rules.append('-' * field_length)
    format = " ".join(["%%-%ss" % l for l in lengths])
    result = [ format % tuple(names), format % tuple(rules) ]
    for row in data:
        result.append(format % tuple(row))
    return "\n".join(result)
def objectMatch(wildcardSearch, objectNamePattern, dbName, cn):
	if wildcardSearch:
		sql = "select '%s' as dbName, s.name schema_name, o.name, o.type from %s.sys.objects o inner join %s.sys.schemas s on o.schema_id = s.schema_id where o.name like '%%%s%%\' order by dbname, schema_name, o.name" % (dbName, dbName, dbName, objectNamePattern)
		cursor = cn.cursor()
		cursor.execute(sql)
		rows = cursor.fetchall()
		if len(rows) > 0:
			print pp(cursor, rows, 1)
	else:
		sql = "select '%s' as dbName, s.name schema_name, o.name, o.type from %s.sys.objects o inner join %s.sys.schemas s on o.schema_id = s.schema_id where o.name = '%s' order by dbname, schema_name, o.name" % (dbName, dbName, dbName, objectNamePattern)
		cursor = cn.cursor()
		cursor.execute(sql)
		rows = cursor.fetchall()
		if len(rows) > 0:
			print pp(cursor, rows, 1)
parser = argparse.ArgumentParser(description='SQL Server object search. Case insensitive')
parser.add_argument('-S', '--server', help='Instance you wish to connect to. myInstance is the default if not specified', dest='instance', default='myInstance')
parser.add_argument('-w', help='Wildcard search indicator. If specified, LIKE clause will be used for pattern matching. Otherwise it will look for an exact match. Exact match is default', action="store_true", dest='wild', default=False)
parser.add_argument('-d', '--database', help='Database(s) you want to search in. If more than one, separate them by comma only. It will search all databases by default', action="store", dest="dbs")
parser.add_argument('objectNamePattern', help='Object name pattern you want to search for', action="store")
argList = parser.parse_args()
try:
	cn = pyodbc.connect("DRIVER={SQL Server};SERVER=%s;DATABASE=master;Trusted_Connection=yes" % argList.instance)
except:
	print "Couldn't connect to %s. It is down or you might have had a typo." % argList.instance
if argList.dbs is None:
	cursor = cn.cursor()
	cursor.execute("select name from sys.databases where state = 0")
	rows = cursor.fetchall()
	for row in rows:
		objectMatch(argList.wild, argList.objectNamePattern, row.name, cn)
else:
	dbs = argList.dbs.split(',')
	for db in dbs:
		objectMatch(argList.wild, argList.objectNamePattern, db, cn)

Comments

« Previous entries

Page optimized by WP Minify WordPress Plugin