Tag: T-SQL

  • Avoiding Stored Procedures

    This editorial was originally published on Aug 13, 2012. It is bein re-published as Steve is on vacation.

    I ran across this piece from a developer on why he avoids stored procedures and thought it made some good arguments. The primary thrust of the piece is that ORM tools (Object-Relational Mapping) have evolved to handle most of the requirements of many applications. They also save a ton of development time since so many stored procedures written are simple CRUD type operations.

    In many cases I agree. Having developers write stored procedures is silly and a waste of time. Procedures that select a few fields, or that update a table based on a primary key are mind-numbingly simple to write, but they take time to get in place. That’s time a developer isn’t spending thinking about the application and logic. Plus with any ORMs and tools like LINQ, you can write one line of code and let the ORM handle all of the work of getting or storing the data. Good points, and in many cases that’s correct. If you do mostly CRUD type work, this is a good reason to perhaps avoid stored procedures and let a tool do the work for you.

    Unless you use a different tool. There are plenty of tools, most of them free, that will generate that CRUD code for you. A few templates or snippets will handle the front end side of the call as well, building code to call stored procedures. If you’re actually typing this stuff over and over, you are wasting time.

    My brain started to wander when I saw “A database should be limited to the role of a persistence layer” which is silly sounding when you move beyond CRUD operations. It completely shut off when I saw “your stored procedures would need to be re-written in order to migrate to MySQL, Oracle or another database” since I think this rarely happens. If it does for you, fine, but the vast majority of apps never leave their initial database.

    There are benefits in ORM tools, but you need to understand how the ORM works, what it’s strengths and weaknesses are. Blindly following the basic pattern for your state-lookup-data-editing dialog for all reporting screens is a sure way to cause yourself some problems. Allowing the ORM to define your relational database, without spending some time thinking about the benefits of good database design and proper modeling is asking for performance problems, or even integrity issues.

    ORM tools are just that tools. Used well, they can perform admirably, but just as I don’t use a hammer to drive a screw into wood, don’t depend on your ORM handling everything database related for you in an efficient manner.

    Steve Jones

  • Output into a Table–#SQLNewBlogger

    Another post for me that is simple and hopefully serves as an example for people trying to get blogging as #SQLNewBloggers.

    I often see people struggling to use triggers for auditing, or having issues with building them to handle multi row updates. However, there’s another choice: the OUTPUT clause.

    Not many people use this clause, but it’s a great way to access the virtual inserted and deleted tables in your code.

    Suppose I have a simple insert statement like this one:

    2016-08-22 11_11_01-SQLQuery7.sql - (local)_SQL2014.Sandbox (PLATO_Steve (57))_ - Microsoft SQL Serv

    I want to ensure that I get the data inserted into an audit table. Certainly I could have code that does the insert into two places, like this:

    2016-08-22 11_13_40-SQLQuery7.sql - (local)_SQL2014.Sandbox (PLATO_Steve (57))_ - Microsoft SQL Serv

    But it can be hard to get developers to use procs like this, they might forget or need to build dynamic SQL. There are the other issues of maintenance where I might update the first INSERT, but forget the second.

    OUTPUT allows me to add a clause in my DML statement. I put this before the FROM clause, or in this case, before the Values clause. Then I include values from inserted or deleted along with any scalar values. That looks like this:

    2016-08-22 11_16_09-SQLQuery7.sql - (local)_SQL2014.Sandbox (PLATO_Steve (57))_ - Microsoft SQL Serv

    However, this returns the data to the console. What I’d really like is to put this into a table. For that reason, I then add an INTO clause, with my table name. Now when I execute this, I get the data from the inserted table added to my EmpAudit table.

    2016-08-22 11_18_32-SQLQuery7.sql - (local)_SQL2014.Sandbox (PLATO_Steve (57))_ - Microsoft SQL Serv

    A basic, quick look at the OUTPUT clause.

    SQLNewBlogger

    I ran into an issue with OUTPUT and realized that I hadn’t ever covered this basic concept for myself. As I learned a few things, I decided to write about OUTPUT. This took me about 10 minutes to cover the basics, and was part of a 30 minute session writing a few more pieces on OUTPUT.

  • Rounding Challenges–#SQLNewBlogger

    Another post for me that is simple and hopefully serves as an example for people trying to get blogging as #SQLNewBloggers.

    Rounding is an interesting activity, and one that I think people sometimes don’t pay enough attention to. Recently I saw a problem that intrigued me and I spent a few minutes looking at the issues.

    Let’s suppose you have data like this for pricing.

    0.3

    0.7

    1.2

    1.6

    How would you round this data? If you use a simple ROUND(n,0) function, you get this:

    0.0

    1.0

    1.2

    2.0

    Now, that’s fine, but what if you want this:

    0.3

    1.0

    1.2

    2.0

    That’s a bit more challenging. I’ll leave the solution out, but note that if I ROUND(n, 1), I get this.

    0.3

    0.7

    1.2

    1.6

    That’s not quite right either. ROUND() is using the number of decimals I’ve given it, but in some cases, I might want to round up. In this case, I only round up when we’re at .5 or higher, but don’t round down. In those cases, I need to limit rounding. This could be done in a WHERE clause, or with a function (be really careful of scalar functions).

    The important part is knowing that I really want this:

    ROUND(0.3, 1)

    ROUND(0.7, 0)

    ROUND(1.2, 1)

    ROUND(1.6, 0)

    How I get these is up to me, and there are a few ways, but really I want to be sure that I understand how ROUND() works and then apply it appropriately for my situation.

    SQLNewBlogger

    This is a quick one, literally about 6 minutes to write. About a minute of that was playing with formatting. Understanding functions, and knowing how they affect data is important to show your knowledge.

    Also, make sure that you know how to solve something like this if you write about it. I’d encourage you to write the solution as well as remember it. Someone might ask you in an interview Winking smile

  • Dropping Masking from a Column–#SQLNewBlogger

    Another post for me that is simple and hopefully serves as an example for people trying to get blogging as #SQLNewBloggers.

    This is a quick one. As I experimented with Dynamic Data Masking for the Stairway to Dynamic Data Masking, and writing my Using SQL Compare with Dynamic Data Masking, I needed to remove masking from a column. I didn’t want to rebuild tables, and hoped there was an easy way to ALTER a column.

    There is.

    Here’s my table. I added masking to this table, but this is the scripted DDL.

    CREATE TABLE [dbo].[OrderHeader](
        [OrderID] [INT] IDENTITY(1,1) NOT NULL,
        [Orderdate] [DATETIME2](3) NULL,
        [CustomerID] [INT] NULL,
        [OrderTotal] [NUMERIC](12, 4) MASKED WITH (FUNCTION = 'default()') NULL,
        [OrderComplete] [TINYINT] NULL,
        [SalesPersonID] [INT] NULL,
    PRIMARY KEY CLUSTERED 
    (
        [OrderID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO

    Note, I have masking enabled on the OrderTotal column. If I want to turn this off, I merely run this:

    ALTER TABLE dbo.OrderHeader
    ALTER COLUMN OrderTotal ADD MASKED WITH (FUNCTION='default()');

    This removes masking, and if I script the table again, I get this:

    CREATE TABLE [dbo].[OrderHeader](
        [OrderID] [INT] IDENTITY(1,1) NOT NULL,
        [Orderdate] [DATETIME2](3) NULL,
        [CustomerID] [INT] NULL,
        [OrderTotal] [NUMERIC](12, 4) MASKED WITH (FUNCTION = 'default()') NULL,
        [OrderComplete] [TINYINT] NULL,
        [SalesPersonID] [INT] NULL,
    PRIMARY KEY CLUSTERED 
    (
        [OrderID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    
    GO

    Easy to turn off if you need to. No application changes, and no security changes.

    SQLNewBlogger

    A quick, handy piece. Give a why you need this, and show how to do it. Any of you could write this in 5 minutes.