Alias for an inner select in both union statement parts - mariadb

Is there way to give an alias for the inner select statement (SELECT defined data) that is used in both parts of union statement:
SELECT T1.CLASS_RATE,T1.BEDS, (T1.CC - T2.CC) RC
FROM (SELECT defined data)
WHERE T2.CC IS NOT NULL
UNION
SELECT T1.CLASS_RATE,T1.BEDS, T1.CC RC
FROM (SELECT defined data)
WHERE T2.CC IS NULL

Short answer: No. (At least not until CTEs in 8.0 or 10.2.)
Long answer: You are working too hard:
SELECT T1.CLASS_RATE, T1.BEDS,
(T1.CC - IFNULL(T2.CC, 0)) RC
FROM defined data

Related

SQLite UNION right only if left is empty

In SQLite there is no such function to UNION only if left expression results empty set.
Consider these as two base tables test and test2:
create table test(
id integer not null primary key,
val integer not null
);
insert into test values(1, 10);
insert into test values(2, 20);
insert into test values(3, 30);
create table test2(
id integer not null primary key,
val integer not null
);
insert into test2 values(1, 100);
insert into test2 values(2, 200);
insert into test2 values(3, 300);
I have this simple query:
select * from test
union
select * from test2;
I want this to always query the first select and the second if (and only if) the first gives empty result.
To illustrate:
select * from test
union
select * from test2;
This shall return all rows from test, and then quit: don't touch test2 at all.
Another sample:
select * from test where val > 50
union
select * from test2;
First query gives empty results, move on and do the second select, shall result all rows from test2.
I want this to be as fast as possible => therefore I don't want to add a subquery for the second select.
Here is the playground.
A SQL SELECT is declarative. It expresses what you want, not how to obtain it. It is up to the db engine to convert it to a query plan. Some SQL dialects allow to hint the engine to do things a certain way, but not SQLite.
The only solution is to query the two part from an imperative language (python, java, C#, ...) that allows you to implements the do something first and check result then do something else
I don't want to add a subquery for the second select.
Well, the two queries are functionally independent in the query, and there is no way for the second query to tell that the first one came up empty, unless you use a subquery.
So, you would typically use a not exists condition in the second query, with a subquery that matches the first query.
So:
select * from test
union all
select * from test2 where not exists (select 1 from test)
Or if there is a where clause in the first query:
select * from test where val > 50
union all
select * from test2 where not exists (select 1 from test where val > 50)

Teradata - Two Joins to a CTE StrTok_Split_To_Table function - Error 3807

This question is related to the excellent answer to this question: Teradata REGEXP_SPLIT_TO_TABLE Input Parameter. Below is the simplest example I can create.
I have a TERADATA query with two CTEs (WITH clauses). The first CTE contains a STRTOK_SPLIT_TO_TABLE function that refers to the second CTE which collects a parameter from the user. The body of the query has a SELECT statement that references the first CTE and gets a column of split parameters. This works great:
WITH
SPLIT_PARAMS(PARAM) AS
(SELECT
TEST_TABLE.SPLIT_PARAMS
FROM
TABLE (StrTok_Split_To_Table(1, PARAMS.INPUT_PARAMS, '|')
RETURNS (outkey INTEGER, TOKENNUM INTEGER, SPLIT_PARAMS VARCHAR(8192) CHARACTER SET Unicode)) AS TEST_TABLE)
,
PARAMS (INPUT_PARAMS) AS
(SELECT
'?InputParams' AS INPUT_PARAMS
)
SELECT
SPLIT_PARAMS.PARAMS
FROM SPLIT_PARAMS
However, I want to be able to refer to the SPLIT_PARAMS CTE more than once. When I do that I get a [3807] object 'PARAMS' does not exist error:
WITH
SPLIT_PARAMS(PARAM) AS
(SELECT
TEST_TABLE.SPLIT_PARAMS
FROM
TABLE (STRTOK_SPLIT_TO_TABLE(1, PARAMS.INPUT_PARAMS, '|')
RETURNS (outkey INTEGER, TOKENNUM INTEGER, SPLIT_PARAMS VARCHAR(8192) CHARACTER SET UNICODE)) AS TEST_TABLE)
,
PARAMS (INPUT_PARAMS) AS
(SELECT
'?InputParams' AS INPUT_PARAMS
)
SELECT
SP1.PARAM,
SP2.PARAM
FROM SPLIT_PARAMS SP1
CROSS JOIN SPLIT_PARAMS SP2
I've tried a bunch of things, like putting two SPLIT_PARAMS subqueries in the main query and using the old-style JOINs referred to in the answer to the previous question. However, any attempt to JOIN to the SPLIT_PARAMS CTE more than once yields this error. (My actual setup is three CTEs deep, but the result is the same - "PARAMS does not exist."
While this may, but doesn't necessarily answer your question, there is really no need for your second CTE here: Simply pushing the input parameter up into the strtok_Split_to_table function will allow you to hit the Split_Params cte more than once:
WITH SPLIT_PARAMS(PARAM) AS
(
SELECT
TEST_TABLE.SPLIT_PARAMS
FROM
TABLE
(
StrTok_Split_To_Table(1, '?InputParams', '|')
RETURNS (outkey INTEGER, TOKENNUM INTEGER, SPLIT_PARAMS VARCHAR(8192) CHARACTER SET Unicode)
) AS TEST_TABLE
)
SELECT
SP1.PARAM,
SP2.PARAM
FROM SPLIT_PARAMS SP1
CROSS JOIN SPLIT_PARAMS SP2

How to reuse a table with UNION?

I am trying to reuse a table in SQLite. My attempt is as follows:
SELECT
Partials.e_sentence
FROM
(SELECT
e_sentence, _id
FROM
Pair
JOIN PairCategories
ON
_id=PairId AND CategoryId=53
UNION
SELECT
e_sentence, _id
FROM
Pair
WHERE
e_sentence LIKE '%' || 'how often' || '%'
GROUP BY
e_sentence)
AS Parents JOIN Partials
ON Parents._id=ParentId
UNION
SELECT
e_sentence
FROM
Parents
The key part I am trying to accomplish is at the bottom, where I try to UNION a table created in the previous statement. Is there a way to do this in SQLite, or am I forced to repeat the query that made the Parents table in the first half of the UNION?
In SQLite 3.8.3 or later, you can use a common table expression:
WITH Parents AS (
SELECT e_sentence, _id
FROM Pair
JOIN PairCategories
...
)
SELECT Partials.e_sentence
FROM Parents
JOIN Partials ON Parents._id = ParentId
UNION
SELECT e_sentence
FROM Parents;
If you're using an older SQLite (probably because you're using an older Android), you can create a view for the subquery:
CREATE VIEW Parents AS
SELECT e_sentence, _id
FROM Pair
JOIN PairCategories
...;
SELECT Partials.e_sentence
FROM Parents
JOIN Partials ON Parents._id = ParentId
UNION
SELECT e_sentence
FROM Parents;
If you do not want to have this view permanently in the database, you could make it temporary (CREATE TEMPORARY VIEW ...) so that it is not available outside the current database connection, or, as last resort, you could just insert the subquery wherever you would use Parent:
SELECT Partials.e_sentence
FROM (SELECT ...) AS Parents
JOIN Partials ON Parents._id = ParentId
UNION
SELECT e_sentence
FROM (SELECT ...) AS Parents;

Using nvarchar(MAX) to build query, but conversion fails in where clause

I have a stored procedure that uses a variable called #Command (nvarchar(MAX)). I then add parameters accordingly based on given input.
declare #Command nvarchar(max)
if(#CaseFileID IS NOT NULL)
BEGIN
select #Command='
select [ServerCredentialsID],[CaseFileID],EIKSLT.[LocationType],EPT.PaymentType,[TaskID],[DateActive]
,[LengthOfPurchase],[Username],[Password],[IPDomain],[Port],[DES],[Website],[AmountPaid],[Latitude]
,[Longitude],[HasAttachments],[TimeStamp],[CaseElement],[Temporary],[StatusID]
FROM Element17a_IKSServerCredentials EIKSSC
JOIN ElementsIKSLocationTypes EIKSLT ON EIKSSC.LocationBeingUsedID= EIKSLT.IKSLocationBeingUsedID
JOIN ElementsPaymentTypes EPT ON EIKSSC.PaymentMethodID=EPT.PaymentTypeID
where EIKSSC.CaseFileID='''+cast(#CaseFileID as nvarchar(MAX))+''' '
#CaseFileID is declared as an int, and in the table it is an int. When I try
where EIKSSC.CaseFileID = ' + #CaseFileID + ' '
then the value doesn't even show (in the error it looks like "EIKSSC.CaseFileID= '" )
I just don't get it.
NOTE: SQL Server 2008 Management Studio
It's because #CaseFileID is VARCHAR even though you don't show it.
Your IF should be
if(#CaseFileID > '')
And if even that doesn't work, then you need to swap to LEFT joins because INNER JOINs will remove records that cannot be matched in the other 2 tables.
Finally, because CaseFileID is an int, you don't need the quotes. Even though SQL Server will implicitly cast '9' to the integer 9 in the WHERE clause, it's just not necessary.
declare #Command nvarchar(max)
if(#CaseFileID > '')
BEGIN
select #Command='
select [ServerCredentialsID],[CaseFileID],EIKSLT.[LocationType],EPT.PaymentType,[TaskID],[DateActive]
,[LengthOfPurchase],[Username],[Password],[IPDomain],[Port],[DES],[Website],[AmountPaid],[Latitude]
,[Longitude],[HasAttachments],[TimeStamp],[CaseElement],[Temporary],[StatusID]
FROM Element17a_IKSServerCredentials EIKSSC
LEFT JOIN ElementsIKSLocationTypes EIKSLT ON EIKSSC.LocationBeingUsedID= EIKSLT.IKSLocationBeingUsedID
LEFT JOIN ElementsPaymentTypes EPT ON EIKSSC.PaymentMethodID=EPT.PaymentTypeID
where EIKSSC.CaseFileID='+cast(#CaseFileID as nvarchar(MAX))

Is it possible to use WHERE clause in same query as PARTITION BY?

I need to write SQL that keeps only the minimum 5 records per each identifiable record in a table. For this, I use partition by and delete all records where the value returned is greater than 5. When I attempt to use the WHERE clause in the same query as the partition by statement, I get the error "Ordered Analytical Functions not allowed in WHERE Clause". So, in order to get it to work, I have to use three subqueries. My SQL looks ilke this:
delete mydb.mytable where (field1,field2) in
(
select field1,field2 from
(
select field1,field2,
Rank() over
(
partition BY field1
order by field1,field2
) n
from mydb.mytable
) x
where n > 5
)
The innermost subquery just returns the raw data. Since I can't use WHERE there, I wrapped it with a subquery, the purpose of which is to 1) use WHERE to get records greater than 5 in rank and 2) select only field1 and field2. The reason why I select only those two fields is so that I can use the IN statement for deleting those records in the outermost query.
It works, but it appears a bit cumbersome. I'd like to consolidate the inner two subqueries into a single subquery. Is this possible?
Sounds like you need to use the QUALIFY clause which is the HAVING clause for Window Aggregate functions. Below is my take on what you are trying to accomplish.
Please do not run this SQL directly against your production data without first testing it.
/* Physical Delete */
DELETE TGT
FROM MyDB.MyTable TGT
INNER JOIN
(SELECT Field1
, Field2
FROM MyDB.MyTable
QUALIFY ROW_NUMBER() (PARTITION BY Field1, ORDER BY Field1,2)
> 5
) SRC
ON TGT.Field1 = SRC.Field1
AND TGT.Field2 = SRC.Fileld2
/* Logical Delete */
UPDATE TGT
FROM MyDB.MyTable TGT
,
(SELECT Field1
, Field2
FROM MyDB.MyTable
QUALIFY ROW_NUMBER() (PARTITION BY Field1, ORDER BY Field1,2)
> 5
) SRC
SET Deleted = 'Y'
/* RecordExpireDate = Date - 1 */
WHERE TGT.Field1 = SRC.Field1
AND TGT.Field2 = SRC.Fileld2

Resources