How to add conditional select in nested SQL query? - sql

From a database table which contains the number of car and sold_date on every particular date, I am trying to calculate the variance of cars sold between 2 ranges of dates.
The problem I face is that I am unable to add an if statement to the nested query (below) that would make the nested statement return a "null" if that condition is false.
SELECT sum(CARS_SOLD) as cars_sold,
(SELECT sum(CARS_SOLD)
FROM <DB>
WHERE date(date_sold) BETWEEN '2023-01-21' AND '2023-01-31'
) as cars_sold_old
FROM <DB>
WHERE date(date_sold) BETWEEN '2023-02-01' AND '2023-02-11'
In the above query, all I want to do is that if the distinct number of dates between the two date ranges is different, the nested statement should return null.
In terms of code:
if (count(distinct(date(date_sold))) between '2023-01-21' AND '2023-01-31' !==
count(distinct(date(date_sold))) between '2023-02-01' AND '2023-02-11')
return null;
Just to clarify, the count in most cases won't be same, I have modified the actual problem in terms of cars_sold but the idea remains the same.

a SELECT COUNT() will return a scalar value which can be comared on equality
SELECT sum(CARS_SOLD) as cars_sold,
(SELECT
IF(
(SELECT count(distinct(date(date_sold))) FROM <DB> WHERE date(date_sold) between '2023-01-21' AND '2023-01-31') =
(SELECT count(distinct(date(date_sold))) FROM <DB> WHERE date(date_sold) between '2023-02-01' AND '2023-02-11') ,
sum(CARS_SOLD) , NULL) sum_
FROM <DB>
WHERE date(date_sold) BETWEEN '2023-01-21' AND '2023-01-31'
) as cars_sold_old
FROM <DB>
WHERE date(date_sold) BETWEEN '2023-02-01' AND '2023-02-11'

Related

Stacking my conditions in a CASE statement it's not returning all cases for each member

SELECT DISTINCT
Member_ID,
CASE
WHEN a.ASTHMA_MBR = 1 THEN 'ASTHMA'
WHEN a.COPD_MBR = 1 THEN 'COPD'
WHEN a.HYPERTENSION_MBR = 1 THEN 'HYPERTENSION'
END AS DX_FLAG
So a member may have more than one, but my statement is only returning one of them.
I'm using Teradata and trying to convert multiple columns of boolean data into one column. The statement is only returning one condition when members may have 2 or more. I tried using Select instead of Select Distinct and it made no difference.
This is a kind of UNPIVOT:
with base_data as
( -- select the columns you want to unpivot
select
member_id
,date_col
-- the aliases will be the final column value
,ASTHMA_MBR AS ASTHMA
,COPD_MBR AS COPD
,HYPERTENSION_MBR AS HYPERTENSION
from your_table
)
,unpvt as
(
select member_id, date_col, x, DX_FLAG
from base_data
-- now unpivot those columns into rows
UNPIVOT(x FOR DX_FLAG IN (ASTHMA, COPD, HYPERTENSION)
) dt
)
select member_id, DX_FLAG, date_col
from unpvt
-- only show rows where the condition is true
where x = 1

Why this sql will cause type conversion error?

WITH tb_testl AS (
SELECT 1 AS id ,'hehe' AS value
UNION ALL
SELECT 1 AS id, '1' AS value
UNION ALL
SELECT 2 AS id, '2' AS value
UNION ALL
SELECT 2 AS id, '2' AS value
), tb_test2 AS (
SELECT CONVERT(INT , value) AS value FROM tb_testl WHERE id = 2
)
SELECT * FROM tb_test2 WHERE value = 2;
this sql will cause error
Conversion failed when converting the varchar value 'hehe' to data
type int.
but the table tb_test2 dosen't have the value 'hehe' which is in the anthor table tb_test1. And I found that this sql will work well if I don't append the statement WHERE value = 2; .I've tried ISNUMBERIC function but it didn't work.
version:mssql2008 R2
With respect to the why this occurs:
There is a Logical Processing Order, which describes the order in which clauses are evaluated. The order is:
FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
You can also see the processing order when you SET SHOWPLAN_ALL ON. For this query, the processing is as follows:
Constant scan - this is the FROM clause, which consists of hard coded values, hence the constants.
Filter - this is the WHERE clause. While it looks like there are two where clauses (WHERE id = 2 and WHERE value = 2). SQL Server sees this differently, it considers a single WHERE clause: WHERE CONVERT(INT , value) = 2 AND id = 2.
Compute scaler. This is the CONVERT function in the select.
Because both WHERE clauses are executed simultaneously, the hehe value is not filtered out of the CONVERT scope.
Effectively, the query is simplified to something like:
SELECT CONVERT(INT, tb_testl.value) AS Cvalue
FROM (
SELECT 1 AS id
, 'hehe' AS value
UNION ALL
SELECT 1 AS id
, '1' AS value
UNION ALL
SELECT 2 AS id
, '2' AS value
UNION ALL
SELECT 2 AS id
, '2' AS value
) tb_testl
WHERE CONVERT(INT, tb_testl.value) = 2
AND tb_testl.id = 2
Which should clarify why the error occurs.
With SQL, you cannot read code in the same way as imperative languages like C. Lines of SQL code are not necessarily (mostly not at all, in fact) executed in the same order it is written in. In this case, it's an error to think the inner where is executed before the outer where.
SQL Server does not guarantee the order of processing of statements (with one exception below). That is, there is no guarantee that WHERE filtering happens before the SELECT. Or that one CTE is evaluated before another. This is considered an advantage because it allows SQL Server to rearrange the processing to optimize performance (although I consider the issue that you are seeing a bug).
Obviously, the problem is in this part of the code:
tb_test2 AS (
SELECT CONVERT(INT, value) AS value
FROM tb_testl
WHERE id = 2
)
(Well, actually, it is where tb_test2 is referenced.)
What is happening is that SQL Server pushes the CONVERT() to where the values are being read, so the conversion is attempted before the WHERE clause is processed. Hence, the error.
In SQL Server 2012+, you can easily solve this using TRY_CNVERT():
tb_test2 AS (
SELECT TRY_CONVERT(INT, value) AS value
FROM tb_testl
WHERE id = 2
)
However, that doesn't work in SQL Server 2008. You can use the fact that CASE does have some guarantees on the order of processing:
tb_test2 AS (
SELECT (CASE WHEN value NOT LIKE '%[^0-9]%' THEN CONVERT(INT, value)
END) AS value
FROM tb_testl
WHERE id = 2
)
error caused by this part of statement
), tb_test2 AS (
SELECT CONVERT(INT , value) AS value FROM tb_testl WHERE id = 2
value has type of varchar and 'hehe' value cannot be converted to integer
WITH tb_testl AS (
SELECT 1 AS id ,'hehe' AS value
UPDATE: sql try convert all value(s) to integer in you statement. to avoid error rewrite statement as
WITH tb_testl AS (
SELECT 1 AS id ,'hehe' AS value
UNION ALL SELECT 1 AS id, '1' AS value
UNION ALL SELECT 2 AS id, '2' AS value
UNION ALL SELECT 2 AS id, '2' AS value
), tb_test2 AS (
SELECT value AS value FROM tb_testl WHERE id = 2
),
tb_test3 AS (
SELECT cast(value as int) AS value FROM tb_test2
)
SELECT * FROM tb_test3

Using a case statement as an if statement

I am attempting to create an IF statement in BigQuery. I have built a concept that will work but it does not select the data from a table, I can only get it to display 1 or 0
Example:
SELECT --AS STRUCT
CASE
WHEN (
Select Count(1) FROM ( -- If the records are the same, then return = 0, if the records are not the same then > 1
Select Distinct ESCO, SOURCE, LDCTEXT, STATUS,DDR_DATE, TempF, HeatingDegreeDays, DecaTherms
from `gas-ddr.gas_ddr_outbound.LexingtonDDRsOutbound_onchange_Prior_Filtered`
Except Distinct
Select Distinct ESCO, SOURCE, LDCTEXT, STATUS,DDR_DATE, TempF, HeatingDegreeDays, DecaTherms
from `gas-ddr.gas_ddr_outbound.LexingtonDDRsOutbound_onchange_Latest_Filtered`
)
)= 0
THEN
(Select * from `gas-ddr.gas_ddr_outbound.LexingtonDDRsOutbound_onchange_Latest`) -- This Does not
work Scalar subquery cannot have more than one column unless using SELECT AS
STRUCT to build STRUCT values at [16:4] END
SELECT --AS STRUCT
CASE
WHEN (
Select Count(1) FROM ( -- If the records are the same, then return = 0, if the records are not the same then > 1
Select Distinct ESCO, SOURCE, LDCTEXT, STATUS,DDR_DATE, TempF, HeatingDegreeDays, DecaTherms
from `gas-ddr.gas_ddr_outbound.LexingtonDDRsOutbound_onchange_Prior_Filtered`
Except Distinct
Select Distinct ESCO, SOURCE, LDCTEXT, STATUS,DDR_DATE, TempF, HeatingDegreeDays, DecaTherms
from `gas-ddr.gas_ddr_outbound.LexingtonDDRsOutbound_onchange_Latest_Filtered`
)
)= 0
THEN 1 --- This does work
Else
0
END
How can I Get this query to return results from an existing table?
You question is still a little generic, so my answer same as well - and just mimic your use case at extend I can reverse engineer it from your comments
So, in below code - project.dataset.yourtable mimics your table ; whereas
project.dataset.yourtable_Prior_Filtered and project.dataset.yourtable_Latest_Filtered mimic your respective views
#standardSQL
WITH `project.dataset.yourtable` AS (
SELECT 'aaa' cols, 'prior' filter UNION ALL
SELECT 'bbb' cols, 'latest' filter
), `project.dataset.yourtable_Prior_Filtered` AS (
SELECT cols FROM `project.dataset.yourtable` WHERE filter = 'prior'
), `project.dataset.yourtable_Latest_Filtered` AS (
SELECT cols FROM `project.dataset.yourtable` WHERE filter = 'latest'
), check AS (
SELECT COUNT(1) > 0 changed FROM (
SELECT DISTINCT cols FROM `project.dataset.yourtable_Latest_Filtered`
EXCEPT DISTINCT
SELECT DISTINCT cols FROM `project.dataset.yourtable_Prior_Filtered`
)
)
SELECT t.* FROM `project.dataset.yourtable` t
CROSS JOIN check WHERE check.changed
the result is
Row cols filter
1 aaa prior
2 bbb latest
if you changed your table to
WITH `project.dataset.yourtable` AS (
SELECT 'aaa' cols, 'prior' filter UNION ALL
SELECT 'aaa' cols, 'latest' filter
) ......
the result will be
Row cols filter
Query returned zero records.
I hope this gives you right direction
Added more explanations:
I can be wrong - but per your question - it looks like you have one table project.dataset.yourtable and two views project.dataset.yourtable_Prior_Filtered and project.dataset.yourtable_Latest_Filtered which present state of your table prior and after some event
So, first three CTE in the answer above just mimic those table and views which you described in your question.
They are here so you can see concept and can play with it without any extra work before adjusting this to your real use-case.
For your real use-case you should omit them and use your real table and views names and whatever columns the have.
So the query for you to play with is:
#standardSQL
WITH check AS (
SELECT COUNT(1) > 0 changed FROM (
SELECT DISTINCT cols FROM `project.dataset.yourtable_Latest_Filtered`
EXCEPT DISTINCT
SELECT DISTINCT cols FROM `project.dataset.yourtable_Prior_Filtered`
)
)
SELECT t.* FROM `project.dataset.yourtable` t
CROSS JOIN check WHERE check.changed
It should be a very simple IF statement in any language.
Unfortunately NO! it cannot be done with just simple IF and if you see it fit you can submit a feature request to BigQuery team for whatever you think makes sense

Oracle: Using Case Statement in Where Clause

In Oracle 12 (and APEX) I am having problems with a CASE statement in a WHERE clause. The scenario is a master table, ORDER, and a PRODUCTS_BOUGHT table, so this is a one to many relationship. I have a report, with a filter on PRODUCTS_BOUGHT. The filter populates a bind variable/APEX page item called :P36_PRODUCT_LISTING. If the user selects a given product, I want the report to just show those orders which contain the given product. The filter contains the word 'All', which should not do any filtering, as well as each product we carry.
My SQL statement is
Select distinct
:P36_PRODUCT_LISTING,
LISTAGG(product_name, ', ') WITHIN GROUP (ORDER BY product_name) OVER (PARTITION BY B.SALES_ORDER_NUMBER) AS products,
...
from ORDER A, PRODUCTS_BOUGHT B
where
A.SALES_ORDER_NUMBER = B.SALES_ORDER_NUMBER
and
case when
:P36_PRODUCT_LISTING = 'All' then 1 = 1
else a.SALES_ORDER_NUMBER IN (SELECT SALES_ORDER_NUMBER from PRODUCTS_BOUGHT where PRODUCT_NAME = :P36_PRODUCT_LISTING) end
When I run the statement, the error I get is Missing Keyword.
What am I doing wrong?
Don't use case. Just use boolean logic:
where (:P36_PRODUCT_LISTING = 'All' or
a.SALES_ORDER_NUMBER IN (SELECT SALES_ORDER_NUMBER
from PRODUCTS_BOUGHT
where PRODUCT_NAME = :P36_PRODUCT_LISTING
)
)
The problem with the case (as you have written it) is that Oracle does not treat the value from a logical expression as a valid value. Some databases do, but not Oracle.
In addition:
Don't use commas in the from clause. Always use proper, explicit join syntax.
Use table aliases that are abbreviations for the table names. Much easier to read the queries and fix bugs.
select distinct should not be necessary here. You are doing an aggregation without a group by, so there is only one row anyway.
So:
Select :P36_PRODUCT_LISTING,
LISTAGG(b.product_name, ', ') WITHIN GROUP (ORDER BY b.product_name) OVER (PARTITION BY B.SALES_ORDER_NUMBER) AS products,
...
from ORDER o join
PRODUCTS_BOUGHT B
on p.SALES_ORDER_NUMBER = B.SALES_ORDER_NUMBER
where (:P36_PRODUCT_LISTING = 'All' or
o.SALES_ORDER_NUMBER IN (SELECT SALES_ORDER_NUMBER
from PRODUCTS_BOUGHT
where PRODUCT_NAME = :P36_PRODUCT_LISTING
)
);
Case is designed to return a value, not a statement..
Try OR instead
where :P36_PRODUCT_LISTING = 'All'
or (:P36_PRODUCT_LISTING <> 'All'
and a.SALES_ORDER_NUMBER IN (SELECT SALES_ORDER_NUMBER from PRODUCTS_BOUGHT where PRODUCT_NAME = :P36_PRODUCT_LISTING))

Oracle SQL re-use subquery "WITH" clause assistance

I have an SQL query in which I need to take the output of a subquery and use it more than once. My existing query works, but only if I repeat the subquery each time I need it. Unfortunately the subquery is complex, and takes time to execute - meaning that multiple iterations really slow the whole thing down.
I have read that you can use the "WITH" statement to assign a subquery output to a variable, in order to re-use that variable. However the problem I'm having is that within the subquery, I need to reference values from the main query. And it appears that if I use WITH - before the main query SELECT - then those references are not recognised. I'll give you a simplified example:
WITH
DateX AS
(
SELECT
MAX(TableSub.Date)
FROM
TableA TableSub
WHERE
TableSub.ID = TableMain.ID
AND TableSub.Event = 'AnotherEvent'
AND TableSub.Date BETWEEN '01-Jan-2015' AND '31-Dec-2015'
)
SELECT
TableMain.ID
FROM
TableA TableMain
WHERE
TableMain.Event = 'MainEvent'
AND TableMain.Date >= DateX
AND (
SELECT
TableSub2.ID
FROM
TableA TableSub2
WHERE
TableSub2.ID = TableMain.ID
TableSub2.Event = 'ThirdEvent'
AND TableSub2.Date <= DateX
) IS NULL
I hope this is clear. It's a simplified version of what I have, but you can see that DateX is used in more than one place: within the main query, and within a subquery. However the problem is that when DateX is defined by WITH, I need to link the ID back to the ID of the main query. And it's not working...
I would be grateful for any advice on this. Am I doing it wrong? Is there a way, or is it just impossible? If so, then should I be using another approach entirely? Thanks.
A better way:
SELECT ID
FROM (
SELECT ID,
"Date",
Event,
LAST_VALUE( CASE Event WHEN 'AnotherEvent' THEN "Date" END IGNORE NULLS )
OVER ( PARTITION BY ID ORDER BY "Date"
ROWS BETWEEN UNBOUNDED PRECEEDING AND UNBOUNDED FOLLOWING
) AS another_date,
FIRST_VALUE( CASE Event WHEN 'ThirdEvent' THEN "Date" END IGNORE NULLS )
OVER ( PARTITION BY ID ORDER BY "Date"
ROWS BETWEEN UNBOUNDED PRECEEDING AND UNBOUNDED FOLLOWING
) AS third_date
FROM TableA
WHERE Event IN ( 'MainEvent', 'ThirdEvent' )
OR ( Event = 'AnotherEvent' AND EXTRACT( YEAR FROM "Date" ) = 2015 )
)
WHERE Event = 'MainEvent'
AND "Date" >= another_date
AND ( third_date IS NULL OR third_date > another_date );
You need to join your DateX CTE on the ID column. Something like:
WITH
DateX AS
(
SELECT
TableSub.ID,
MAX(TableSub.Date) AS MaxDate
FROM
TableA TableSub
WHERE
AND TableSub.Event = 'AnotherEvent'
AND TableSub.Date >= DATE '2015-01-01'
AND TableSub.Date < DATE '2016-01-01'
GROUP BY
TableSub.ID
)
SELECT
TableMain.ID
FROM
TableA TableMain
JOIN
DateX
ON
DateX.ID = TableMain.ID
WHERE
TableMain.Event = 'MainEvent'
AND TableMain.Date >= DateX.MaxDate
AND (
SELECT
TableSub2.ID
FROM
TableA TableSub2
JOIN
DateX
ON
DateX.ID = TableSub2.ID
WHERE
TableSub2.ID = TableMain.ID
TableSub2.Event = 'ThirdEvent'
AND TableSub2.Date <= DateX.MaxDate
) IS NULL
The CTE also needs a column alias for the aggregate; and as you need to join in the ID, you need to include that and group by it.
The last subquery looks odd; you might want NOT EXISTS rather than IS NULL if you're looking for no record. Perhaps your real query is using an aggregate, but even so that might be quicker.
This still may not be the best approach but it's hard to tell from your example. Hitting the same table three times may be unnecessarily expensive.