Friday, August 30, 2019

Duplicate columns in order by

Another hard-to-understand anti-feature of MS SQL Server presents itself in an order by clause. SQL Server actively disallows a single column to be present multiple times in an order by.
Why, why, why?
Why can't you just ignore all but first occurence of a column?

select * 
from (
    select 2 as x union all
    select 4 as x union all
    select 1 as x union all
    select 3 as x
) dataset
order by x, x;
Msg 169, Level 15, State 1, Line 105
A column has been specified more than once in the order by list. Columns in the order by list must be unique.


Oracle does not care about it:
select * from (
    select 2 as x from dual union all
    select 4 as x from dual union all
    select 1 as x from dual union all
    select 3 as x from dual
) dataset
order by x, x;
         X
----------
         1
         2
         3
         4


Again, I expect to hear, it's not a problem to delete/comment out part of your code. It's not, but it's very often to move columns around when prototyping, especially, temporary moving column from back of the list to beginning. Normally I'd copy the column name, put it in front, run and delete. With MS SQL you have to do more steps and still, sooner or later you miss one or two places and see above mentioned error. If you add this to other MS SQL Server order by issues... eh...

Best.

Wednesday, August 28, 2019

Table Functions

Hi,

Although there's a lot to hate about SQL Server not everything is bad there. In fact, there's quite a lot other database engines could implement... at least Oracle could. More on it later.
This time I'm more interested in the functionality that both SQL Server and Oracle have already in place but, from ease-to-implement point of view, SQL Server does it better (minus T-SQL syntax of course ;D) - Table Functions (or, Table Valued User Defined Function as Microsoft decided to call it).

What is the Table Function? This is the question we have to ask in the first place. Well, Table Function is a function that returns some kind of collection and can be used just like any table or view, as part of a typical DML select statement. From developer's point of view, it can be seen as a sort of parameterized view although, being a function, it allows a sort of random operations to be done in it's body. You can do whatever the RDBMS allows you to do in regular functions. What is widely used as an example is parsing a clob data, usually a kind of XML or JSON data resulting in multiple output rows. Generally, Table Functions are not amongst the most used tools in databases nowadays but when you get to the point when you need them, you'll be relieved it's there.

SQL Server allows for two different Table Function syntaxes. Both are useful but serve a little bit different purpose. First one, much easier to implement is an Inline Table-Valued Function. In this case the function really serves as a parameterized view. The syntax allows only for a select statement and goes like this (the syntax comes from official documentation):

-- Transact-SQL Inline Table-Valued Function Syntax  
CREATE [ OR ALTER ] FUNCTION [ schema_name. ] function_name  
( [ { @parameter_name [ AS ] [ type_schema_name. ] parameter_data_type  
    [ = default ] [ READONLY ] }  
    [ ,...n ] 
  ] 

RETURNS TABLE 
    [ WITH <function_option> [ ,...n ] ] 
    [ AS ] 
    RETURN [ ( ] select_stmt [ ) ] 
[ ; ]


In practice you'll generally get whatever comes from the select, nothing more, nothing else. It's extremely easy to code but have a quite limited use.
That's how it can look in practice:
create or alter function MyInlineFunction ( @mp int )
returns table as
return
(
 select p.p*@mp as p, q.q*@mp as q
 from (
  select 1 as p union all
  select 2 as p union all
  select 3 as p
 ) p
 cross join
 (
  select 1 as q union all
  select 2 as q union all
  select 3 as q
 ) q
)
go

select * from MyInlineFunction( 2 )
p           q
----------- -----------
2           2
2           4
2           6
4           2
4           4
4           6
6           2
6           4
6           6


We can already see that no matter what we do, we will always get 9 rows in the output data set. How can we overcome it? By using the other, Multi-Statement Table-Valued Function syntax, obviously.

It's syntax goes like this:
-- Transact-SQL Multi-Statement Table-Valued Function Syntax 
CREATE [ OR ALTER ] FUNCTION [ schema_name. ] function_name  
( [ { @parameter_name [ AS ] [ type_schema_name. ] parameter_data_type  
    [ = default ] [READONLY] }  
    [ ,...n ] 
  ] 

RETURNS @return_variable TABLE <table_type_definition> 
    [ WITH <function_option> [ ,...n ] ] 
    [ AS ] 
    BEGIN  
        function_body  
        RETURN 
    END 
[ ; ]


So... we could code such a function like this:
create or alter function MyMultiStatementFunction ( @pcount int, @qcount int, @mp int )
RETURNS @t TABLE ( 
 p int,
 q int 
)
AS
begin
 declare @p_iterator int = 1
 declare @q_iterator int = 1

 while @p_iterator <= @pcount
 begin
  while @q_iterator <= @qcount
  begin
   insert into @t (p, q) values ( @p_iterator*@mp, @q_iterator*@mp )
   set @q_iterator = @q_iterator + 1
  end
  set @q_iterator = 1
  set @p_iterator = @p_iterator + 1
 end

 return
end
go

select * from MyMultiStatementFunction( 3, 3, 2 )
This gives us exactly the same result as the previous one, but now, we can play around with first two parameters to get different number of rows.

The syntax still doesn't look bad. It's... surprisingly easy! You just define the output type as a table, insert rows inside the function and return it using a single return statement. It really can't get easier than this!

In the Oracle world however, all is of the latter type but there's so many details a developer have to think of that it requires quite a lot of reading and education. First of all, you have an easy (which is still harder and more cumbersome than MS SQL) way of creating table functions but in this case... well... you need to know how does it work internally. The easy version creates a collection inside a function, caches it and returns the whole thing once the function is finished. This behaviour may require substantial amounts of memory and leads to the situation when query appears to be hung up since no rows are produced during processing. The general cumbersomness of PL/SQL table function comes from the fact that, unlike MS SQL which declares return type in function declaration, PL/SQL requires those types to exist in the database. Duh... although, 12.1 allows them to exist in a package so, there's a light..
How would the obove function look like in PL/SQL then?
create or replace type t_pq_r is object 
( 
    p number,
    q number 
)
/

create or replace type t_pq is table of t_pq_r;
/

create or replace function MyOracleTableFunction ( pcount number, qcount number, mp number ) 
return t_pq
is
    piterator number := 1;
    qiterator number := 1;
    rw t_pq_r;
    rv t_pq;
begin
    rv := t_pq();
    while piterator <= pcount loop
        while qiterator <= qcount loop
            rw := t_pq_r( piterator*mp, qiterator*mp );
            rv.extend( 1 );
            rv( rv.count ) := rw;
            qiterator := qiterator + 1;
        end loop;
        qiterator := 1;
        piterator := piterator + 1;
    end loop;
    return rv;
end;
/

select * from TABLE ( MyOracleTableFunction( 3, 3, 2 ) );

Although not overly complicated it doesn't look nearly as nice as SQL Server version. It is very similar to what you'd probably see in other languages (Java, C++, etc) when building collections, though.

So, what can we do to make it more interactive in terms of getting rows? We could pipeline it.
create or replace function MyOracleTablePipelinedFunction ( pcount number, qcount number, mp number ) 
return t_pq pipelined
is
    piterator number := 1;
    qiterator number := 1;
    rw t_pq_r;
begin
    while piterator <= pcount loop
        while qiterator <= qcount loop
            rw := t_pq_r( piterator*mp, qiterator*mp );
            PIPE ROW(rw);
            qiterator := qiterator + 1;
        end loop;
        qiterator := 1;
        piterator := piterator + 1;
    end loop;
    return;
end;
/

select * from TABLE ( MyOracleTablePipelinedFunction( 3, 3, 2 ) );
Now, we get results instantanously. It's so much quicker all in all...

I did some research and own testing and... initially I said SQL Server is better in this regards, now I'm drifting towards another no, it isn't.

I run Oracle jobs on the small db.t2.micro (1 CPU, 1GB RAM) instance. On the other hand, SQL Server is executed locally on 8 CPU, 32GB RAM machine... What are the times of executing each of those functions (the ones using 3 parameters)?

And mind you, Oracle does not cache results of executions of Table Functions: "Multiple invocations of a table function, either within the same query or in separate queries result in multiple executions of the underlying implementation. That is, in general, there is no buffering or reuse of rows."

It's what I got:



On the x axis is the value of the p/q parameters (both got the same value), on the y axis is the elapsed time (the scale is logarithmic). What does it tell us? That Oracle pipelined execution is unbeatable here and MS SQL is roughly comparable (in terms of performance) to the basic Oracle way of doing this stuff however, after certain threshold (~1000 for p and q) Oracle is becoming unusable whereas MS didn't experience any problems.

So, all in all, I dare to say, Oracle wins once again. It's easier to do in SQL Server, yes. Do I wish Oracle did not need explicit type definitions? Yes, I do. But despite all of it the performance difference is so much favorable for Oracle it's a no brainer.

One final disclaimer. I haven't read all the books, whitepapers, didn't do courses and who knows what, so there's a chance that there are some magic tricks that make table functions to perform better in MS SQL Server. If so, don't hesitate to clear that. I know also that those functions could have been written in trillion other ways. If you wish to present better (in any terms) versions - be my guest!

Best

Monday, August 26, 2019

Packages (procedures/functions)

I was recently asked to compare differences in T-SQL and PL/SQL packages. At least that's what I understood. Since, T-SQL does not support anything virtually resembling PL/SQL packages it may seem pointless. I disagree, however. I did some research and found, rather interesting (at least socially), discussion on StackOverflow. There is some valid points made there but mostly all voices against PL/SQL packages come from people who seem to be very uneducated developers. It's also pretty old topic so maybe Oracle was so much different back then? No, it wasn't ;)

So, instead of just finishing with - SQL Server doesn't support packages, which, by itself is both true and something SQL Server can be hated on, I decided to elaborate a bit on PL/SQL packages and why these are so much better then, yet another, bollocks workaround T-SQL developers are forced to make.

So, what's the package? I'm going to directly quote gadsUK here:

"The Package has two elements: a header file and a body file. The header file is your public interface, and contains the signature (name, params and return type if applicable) of all the stored procedures or functions (in Oracle a function returns a value, a stored proc doesn't) that are directly callable. The package body must implement all the procedure signatures in the package header file.
The body element of the package contains all the stored procs and logic that actually do the work. You may have a Save procedure declared in the package header that calls an insert or update proc that exists in the body. The developer can only see the "Save" proc. It's important to keep in mind that the package body can also implement procs or functions not declared in the package header, they're just not accessible outside of the package itself."

So, unlike some seems to be suggesting, it's not only used for grouping code. It's more of a single entity, a class. Remember, you can keep package variables and constants there as well so it's possible to share it between sessions or use it as a Singleton if need be. It's all well documented. It's not impossible to mimic such behaviour in SQL Server but you would have to code it manually, probably using temporary tables.

Some say, PL/SQL packages have this huge installation drawback. Let me quote gunnar here:

"The best argument against oracle packages is that based on experience and research on the Ask Tom site, one can't update a package without taking it off line. This is unacceptable. With SQL Server, we can update stored procedures on the fly, without interrupting production operation."

This is absolute bollocks as it has been in 2012 and 2014 (when the comment has been made). I don't know if Gunnar never used Oracle or still had been using some pre 10 version. I suppose, the issue he is reffering to is inability to compile packages in certain situations. Those situations are changing with Oracle version and... the same happens for regular procedures and function. Hell... to most of the database objects really! What they're talking about is the fact that you can't compile the package when it's being used. That means - if there are any sessions running any procedures/functions from the package. The same applies to regular procedures and functions as well. And also, you can't run any DDLs on currently used objects. That alone means that this opinion doesn't have any sense.

How did it change with Oracle versions? Well, in 10i, you couldn't compile a package if it was referenced in anything that was being executed at the time of compilation. In 11g, it's not possible only if the particular package is active. I think in 12-something or some next version it'll be possible to compile package at any time (obviously only new sessions/executions will see new package) so the issue is disappearing anyway.

Also shout-out to belgariontheking for most pointless and most uneducated opinion on the topic ever. You should switch to some other job, bro. You're clearly not fit for the Oracle. Maybe find SQL Server position somewhere :P

 Best



Friday, August 23, 2019

SQL Brainf**k, not for faint hearted!

This is a challenge. I'll start with some background of the problem, challenge query and the explanation.

If you know already know the answer - good! If not and want to explain it yourself, don't browse far down.

Have fun!

Ok, let's go.

I have to admit, at first I thought it's a bug in SQL Server. I was doing some data dictionary work in SQL Server and came up with simple query to check database log file size:
select size
from sys.master_files 
where type=1 
and database_id=(select database_id from sys.databases where name='master') 
and name='mastlog'
It'll give you one row (as long as you provide consistent conditions, the example should work everywhere).


but I made a mistake, a typo... (not possible to make with Oracle :P) I left out from statement from the subquery resulting in:
select size
from sys.master_files 
where type=1 
and database_id=(select database_id where name='master') 
and name='mastlog'
and oh boy, how surprised it was for me not to see any rows! I checked conditions - both database and file names were valid and consistent! I should have gotten the single value! Then I realised my mistake and immidiately thought - how stupid SQL Server is... again! That would have never happened in Oracle since it's enforcing the use of the from statement. I would've used the table name there. So, I thought, another good topic for the blog :D So I started building demo query that I could run also in Oracle... I've almost lost concience when it worked exactly the same!

So the challenge is - explain why it works as it does and if it's ok or not.

The query is built using with syntax. There are two datasets defined in with section.
One is a dataset (a combination of two columns, 9 rows total):
select * from
( 
 select 'A' as k union all 
 select 'B' as k union all 
 select 'C' as k
) k
cross join
( 
 select 1 as v union all 
 select 2 as v union all 
 select 3 as v
) v
K          V
- ----------
A          1
A          2
A          3
B          1
B          2
B          3
C          1
C          2
C          3


The other one is a list to be used for filtering the above mentioned dataset:
select 'A' as k, 0 as v union all 
select 'B' as k, 1 as v union all 
select 'C' as k, 0 as v
K          V
- ----------
A          0
B          1
C          0


We want to filter the dataset on column k with rows from list where v equals 1, so our working query is:
-- SQL Server:
with 
 dataset as (
  select * from
  (
   select 'A' as k union all 
   select 'B' as k union all 
   select 'C' as k) k
  cross join
  (
   select 1 as v union all 
   select 2 as v union all 
   select 3 as v) v
 ),
 list as (
  select 'A' as k, 0 as v union all 
  select 'B' as k, 1 as v union all 
  select 'C' as k, 0 as v
 )
select * 
from dataset 
where k = (select k from list where v=1 )
go

-- Oracle
with 
 dataset as (
  select * from
  (
            select 'A' as k from dual union all 
            select 'B' as k from dual union all 
            select 'C' as k from dual) k
  cross join
  (
            select 1 as v from dual union all 
            select 2 as v from dual union all 
            select 3 as v from dual) v
 ),
 list as (
  select 'A' as k, 0 as v from dual union all 
        select 'B' as k, 1 as v from dual union all 
        select 'C' as k, 0 as v from dual
 )
select * 
from dataset 
where k = (select k from list where v=1 );

...and, as expected, we're getting this as an output:
K          V
- ----------
B          1
B          2
B          3


The question is, why the crippled query (same as above but with removed from list part in SQL Server or replaced list with dual in case of Oracle) returns this:
-- SQL Server:
with 
 (...)
select * 
from dataset 
where k = (select k where v=1 )
go

-- Oracle
with 
 (...)
select * 
from dataset 
where k = (select k from dual where v=1 );
K          V
- ----------
A          1
B          1
C          1


Please also note, that the subquery, when executed independently, fails!

? ? ? ? ? ? ? ? ? ?





Best


Thursday, August 22, 2019

Procedure parameters (expressions)

We're on the roll so let's continue with SQL Server's function and procedure parameters.

We've finished last post with:
declare @rv float
declare @v1 float = 2
declare @v2 float = 3
EXECUTE @rv = [dbo].[StupidFunction] @v1, @v2
print @rv
declare @exp bit = 1
EXECUTE @rv = [dbo].[StupidFunction] @v1, @v2, @exp
print @rv
but then you must:
- design everything to use this syntax right from the beginning
- you can't use this function in plain SQL
- you're getting yourself into other T-SQL problem with parameters which is a topic for another post

What exactly do I mean by T-SQL problem with parameters? Well... probably all T-SQL developers will tell me this is bollocks. Maybe it is, maybe it's not. It is for me, certainly. Maybe it's not a big deal but an irritating behaviour nevertheless.
The problem I'm referring here is inability to provide expressions as procedure parameters (or functions executed via EXECUTE statement). You can only use variables and constants (at least that)...

In real life, you're forced to constantly pass everything through clouds of variables, exactly as in above mentioned example (obviously, in this particular case we could have used constants, it's not the point here).

What is a bit funny, although absolutely understandable and even required is that there's no such problem when using functions:
select v1, v2, v3, [dbo].[StupidFunction] ( v1*v2, v3, default ) as rvStupidFunction
from (select 2 as v1, 2 as v2, 2 as v3) as dataset
v1          v2          v3          rvStupidFunction
----------- ----------- ----------- ----------------------
2           2           2           8


This just has got to work as this is SQL and SQL itself doesn't have such limitation. Is this because of this awkward T-SQL script-like syntax?

This just doesn't work here:
declare @rv float
declare @v1 float = 2
declare @v2 float = 2
declare @v3 float = 2
EXECUTE @rv = [dbo].[StupidFunction] @v1*@v2, @v3
print @rv

To make it work you have to create another variable and set its value, like so:
declare @rv float
declare @v1 float = 2
declare @v2 float = 2
declare @v3 float = 2
declare @v1calc float=@v1*@v2  -- at least this can be a one liner...
EXECUTE @rv = [dbo].[StupidFunction] @v1calc, @v3
print @rv

It's just a non existing problem in Oracle:
declare
    v1 number := 2;
    v2 number := 2;
    v3 number := 2;
begin
    dbms_output.put_line ( StupidFunction ( v1*v2, v3 ) );
end;
/



Did I mention I also dislike prefixing variables with at sign (@)? It helps, however, a bit with the clarity as you know right away what is a variable and what's not. Still, not a fan. Do C/C++, Java, C#, PL/SQL and myriads of other languages need any such prefixing? That's what I thought :)

Wednesday, August 21, 2019

Function parameter's defaults

There's a lot to be said about differences between functions/procedures in T-SQL and PL/SQL. This time we're gonna focus on function parameters and, surprise, how bad T-SQL is in this regard. Specifically when using default values.

Let's start with a simple, pointless function:
create or alter function [dbo].[StupidFunction] ()
returns float
as begin
 return 1.0;
end
go
It does what it's intended to do, returns 1:
select [dbo].[StupidFunction] ()
----------------------
1


I want to talk about parameters but this function has none. Let's add some. Let's say this function will be multiplying two numbers that are provided as its parameters:
create or alter function [dbo].[StupidFunction] (
 @num1 float,
 @num2 float
)
returns float
as begin
 return @num1 * @num2;
end
go
There's no way it won't work:
select [dbo].[StupidFunction] ( 2, 3 ) as rvStupidFunction
rvStupidFunction
----------------------
6


And just for a good measure, let's try executing this in T-SQL:
declare @rv float
set @rv = [dbo].[StupidFunction] ( 2, 3 );
print @rv
and again, no problems there, we get a simple 6 in the output.

The same in the Oracle world would be:
create or replace function StupidFunction ( num1 in number, num2 in number ) 
return number
is
begin
    return num1 * num2;
end;
/

select StupidFunction ( 2, 3 ) as rvStupidFunction from dual;

begin
    dbms_output.put_line ( StupidFunction ( 2, 3 ) );
end;
/


Let's say that we've used this function in multiple places but now we want it to make more but, for some reason, we don't want to create new function and duplicate it's logic, we want to add new parameter here. The parameter will tell the function if it should multiply or exponentiate:
create or alter function [dbo].[StupidFunction] (
 @num1 float,
 @num2 float,
 @expn bit
)
returns float
as begin
 declare @rv float
 if @expn=0 
  set @rv = @num1 * @num2;
 else 
  set @rv = power(@num1, @num2);

 return @rv;
end;
go

Ok, let's see how it behaves now:
select [dbo].[StupidFunction] ( 2, 3, 0 ) as rvStupidFunction
select [dbo].[StupidFunction] ( 2, 3, 1 ) as rvStupidFunction

declare @rv float
set @rv = [dbo].[StupidFunction] ( 2, 3, 0 );
print @rv
set @rv = [dbo].[StupidFunction] ( 2, 3, 1 );
print @rv
rvStupidFunction
----------------------
6

(1 row affected)

rvStupidFunction
----------------------
8

(1 row affected)

6
8


Cool, looks great, isn't it?
Let's see how our old code works, shall we?
select [dbo].[StupidFunction] ( 2, 3 ) as rvStupidFunction

declare @rv float
set @rv = [dbo].[StupidFunction] ( 2, 3 );
print @rv
Msg 313, Level 16, State 2, Line 24
An insufficient number of arguments were supplied for the procedure or function dbo.StupidFunction.


Wait... What? Hmmmm.....
Ok,  that makes sense, we now have additional parameter that we haven't used here but I don't want to make ANY amends to already existing code. What should I do?
Yup - I should have defaulted the new parameter to some value. The only sane option is to make it work as before so the default should be 0 (false). Let's see how it behaves now:
create or alter function [dbo].[StupidFunction] (
 @num1 float,
 @num2 float,
 @expn bit = 0
)
returns float
as begin
 declare @rv float
 if @expn=0 
  set @rv = @num1 * @num2;
 else 
  set @rv = power(@num1, @num2);

 return @rv;
end;
go

select [dbo].[StupidFunction] ( 2, 3 ) as rvStupidFunction
Msg 313, Level 16, State 2, Line 24
An insufficient number of arguments were supplied for the procedure or function dbo.StupidFunction.


What? Again?

Let's look in the docs...
Oh, I should have used a special keyword now...
select [dbo].[StupidFunction] ( 2, 3, default ) as rvStupidFunction

declare @rv float
set @rv = [dbo].[StupidFunction] ( 2, 3, default );
print @rv

Now it works:
rvStupidFunction
----------------------
6

(1 row affected)

6



B u t   w h a t ' s   t h e   p o i n t ?

Why we want to use defaults anyway? What's their inteded use? First of all we use it for new parameters, when we do have some working code and we need to extend it with some functionality and save us from the cost and risk of amending too much of existing code, we want to make transparent changes. In T-SQL it's virtually impossible so WHAT'S THE POINT? Why does the SQL Server even mentions this crippled functionality?

There's just one (and only), very cumbersome way to make it work in the sane way which is by using the EXECUTE statement:
declare @rv float
declare @v1 float = 2
declare @v2 float = 3
EXECUTE @rv = [dbo].[StupidFunction] @v1, @v2
print @rv
declare @exp bit = 1
EXECUTE @rv = [dbo].[StupidFunction] @v1, @v2, @exp
print @rv
but then you must:
- design everything to use this syntax right from the beginning
- you can't use this function in plain SQL
- you're getting yourself into other T-SQL problem with parameters which is a topic for another post

How does it work in Oracle? Easy:
create or replace function StupidFunction ( num1 in number, num2 in number, expn in number := 0 ) 
return number
is
    rv number;
begin
    if expn = 0 then
        rv := num1 * num2;
    else
        rv := power(num1,num2);
    end if;
    return rv;
end;
/

select StupidFunction ( 2, 3 ) as rvStupidFunction from dual;
select StupidFunction ( 2, 3, expn=>1 ) as rvStupidFunction from dual;

begin
    dbms_output.put_line ( StupidFunction ( 2, 3 ) );
    dbms_output.put_line ( StupidFunction ( 2, 3, expn=>1 ) );
end;
/

Function STUPIDFUNCTION compiled

RVSTUPIDFUNCTION
----------------
               6

RVSTUPIDFUNCTION
----------------
               8

6
8
PL/SQL procedure successfully completed.



That's how we roll...
Best


Tuesday, August 20, 2019

Inconsistency in literals as paremetrs for built in functions

Well, this is not really a problem but it hurts my eyes nevertheless.

Is there anyone who could tell me why Microsoft decided that for some functions some of the parameters will allow values (as first parameter for DateAdd for example) as literals only when in similar cases other functions accept strings (Format)?
select getDate(), DateAdd( day, -1, getDate() ), Format( 0.1234, 'P2' )
----------------------- ----------------------- ------
2019-08-19 13:08:12.900 2019-08-18 13:08:12.900 12.34%


(1 row affected)



This is just another case of SQL Server's inconsistency.


I, generally dislike constant literals as parameters. Why? Because these are a kind of eternal identities with no shape or form. You may have impression that these also exist in Oracle but these are probably system variables/constants which do have their names, datatypes an values (just like user variable).

I just gave it a second thought and it IS a problem. Let's take a look at the documentation for above mentioned DateAdd function. What does it say in bold? This:

"DATEADD does not accept user-defined variable equivalents for the datepart arguments."

That's about it.

Let's imagine we have some kind of configuration table that's gonna be used in some kind of date operations, let's say in calculating next valid date. It could be easily used in such calculations as is but no, you'll be forced to case the whole formula repeating DateAdd multiple times.


Monday, August 19, 2019

No table select

Every now and then everyone using SQL is facing a problem that requires some static data to be used in a query. It may be just some constant in a column or it maybe the whole dataset. First of those cases does not require any discussion. You just use a static value in a query giving it a name (it's not really required but more often then not you'll use it in a subquery):
select some_column, another_colum, 1 as constant_column
from some_table;
Obviously, it's not what this post is about. I want to talk about the case when all columns are constant. How to create such select when there is no table to query from?

It's easy. In case of SQL Server you just get rid of the from keyword (and all that's associated with it, obviously):
select 1 as x;
x
-----------
1

(1 row affected)


Things are a bit different in Oracle as Oracle have a special no-column-one-row dummy table called dual. You can query anything from this table exactly as you would do in the first example:
select 1 as x from dual;
         X
----------
         1

You can also query star (*) from it :)
select * from dual;
What you're getting is a single column, single row output:
DUMMY         
-----------
X



What are the pros and cons of each of those approaches?

For sure, Oracle enforces unified query structure where you have to use all the basic keywords of SQL which are SELECT and FROM. In my opinion the query that is stripped to just a SELECT keyword looks horrible. This is a very strong argument I'm making here ;) I'm sure, that enforcing structural integrity throughout all forms of queries made Oracle internals cleaner, easier to maintain.
It's quite common to block many of such selects into more rows with UNION ALL and then SQL Server's version is just smaller, a little bit more on the cleaner side. My personal preference though is that I like consistency more therefore - Oracle.


By the way.
I've said this couple times before - many commenters on the internet seem not to grasp the reasons why some concepts exist. They often give simple, irrelevant answers. No select queries are not used only for proving/checking how database works but those are very useful for list of values cross-or-full-outer-joined to original data to provide complete set data cells. For example - we might have some finance data and we want to group and summarize it by quarter and category. What we also know is that not every category has releveant data for each of the quarters. How would we solve it (apart from using any reporting tools ;D)?
We can cross/full outer join it with a static list, like so:
Oracle:
create table FINANCE_DATA as
select 'Q1' as quarter, 'A' as item_category, 123 as amount from dual union all
select 'Q2' as quarter, 'A' as item_category, 234 as amount from dual union all
select 'Q4' as quarter, 'B' as item_category, 456 as amount from dual union all
select 'Q1' as quarter, 'B' as item_category, 567 as amount from dual union all
select 'Q2' as quarter, 'C' as item_category, 678 as amount from dual union all
select 'Q4' as quarter, 'C' as item_category, 890 as amount from dual;


select q.quarter, nvl(f.total, q.amount) as total
from (
    select quarter, sum(amount) as total 
    from FINANCE_DATA 
    group by quarter
)f
right join
(
    select 'Q1' as quarter, 0 as amount from dual union all
    select 'Q2' as quarter, 0 as amount from dual union all
    select 'Q3' as quarter, 0 as amount from dual union all
    select 'Q4' as quarter, 0 as amount from dual
) q
on (f.quarter = q.quarter)
order by q.quarter
;
SQL Server:
select *
into FINANCE_DATA
from (
  select 'Q1' as quarter, 'A' as item_category, 123 as amount union all
  select 'Q2' as quarter, 'A' as item_category, 234 as amount union all
  select 'Q4' as quarter, 'B' as item_category, 456 as amount union all
  select 'Q1' as quarter, 'B' as item_category, 567 as amount union all
  select 'Q2' as quarter, 'C' as item_category, 678 as amount union all
  select 'Q4' as quarter, 'C' as item_category, 890 as amount 
) as f;
go



select q.quarter, isnull(f.total, q.amount) as total
from (
    select quarter, sum(amount) as total 
    from FINANCE_DATA 
    group by quarter
)f
right join
(
    select 'Q1' as quarter, 0 as amount union all
    select 'Q2' as quarter, 0 as amount union all
    select 'Q3' as quarter, 0 as amount union all
    select 'Q4' as quarter, 0 as amount 
) q
on (f.quarter = q.quarter)
order by q.quarter
;

You would be amazed how often it's used among different reporting tools.

Best


Non fractional division (int/float)

For me a RDBMS is a high level tool. Something very opposite to using low level languages like C, C++ or... Assembler. From user/developer p...