I have three tables A,B and C. I have to detect if any of them have zero rows. As soon as any table with zero row is detected, I do not need to check other ones.
So, one way is I execute three queries separately and after each query I check the number of returned rows. If its non-zero then only I execute the query of next table.
Second way is I write a single query using case-when, something like
select case
when (select count(*) from A = 0)
then 1
else (
select case
when (select count(*) from B = 0)
then 1
else (
select case
when (select count(*) from B = 0)
then 1
else 0
)
)
end as matchResult;
The second method requires lesser code as I have to write a single query and db will do the comparison for me.
My question is whether its overkilling or can I further optimize the query?
EDIT
On further study, I realise that the query above is wrong. However, I can simply do it as
select case
when (select count(*) from A) = 0 and
(select count(*) from B) = 0 and
(select count(*) from C) = 0
then 1
else 0
end as matchResult;
and if I am not wrong, and conditions are checked from left to right and if any one is false, conditions to the right are not checked.
Please confirm this point.
Count is kind of expensive
select 1
where not exits (select * from a)
or not exits (select * from b)
or not exits (select * from c)
One query with three resutls:
select (select count(*) from A) as Acount,
(select count(*) from B) as Bcount,
(select count(*) from C) as Ccount
This instead gives name of the fitst table that is empty:
select case
when (select count(*) from A)=0 then 'A'
when (select count(*) from B)=0 then 'B'
when (select count(*) from C)=0 then 'C'
else 'ops, all have records' -- remove this to have a null
end as first_empty_table
Related
Say I have a table t with 2 columns:
a int
b int
I can do a query such as:
select b
from t
where b > a
and a in(1,2,3)
order by b
where 1,2,3 is provided from the outside.
Obviously, the query can return no rows. In that case, I'd like to select everything as if the query did not have the and a in(1,2,3) part. That is, I'd like:
if exists (
select b
from t
where b > a
and a in(1,2,3)
)
select b
from t
where b > a
and a in(1,2,3)
order by b
else
select b
from t
where b > a
order by b
Is there a way to do this:
Without running two queries (one for exists, the other one the actual query)
That is less verbose than repeating queries (real queries are quite long, so DRY and all that stuff)
Using NOT EXISTS with a Sub Query to Determine if condition exists
SELECT b
FROM
t
WHERE
b > a
AND (
NOT EXISTS (SELECT 1 FROM #Table WHERE a IN (1,2,3))
OR a IN (1,2,3)
)
ORDER BY
b
The reason this works is because if the condition exists then the OR statement will include the rows and if the condition does not exist then the NOT EXISTS will include ALL rows.
Or With Common Table Expression and window Function with Conditional Aggregation.
WITH cte AS (
SELECT
b
,CASE WHEN a IN (1,2,3) THEN 1 ELSE 0 END as MeetsCondition
,COUNT(CASE WHEN a IN (1,2,3) THEN a END) OVER () as ConditionCount
FROM
t
)
SELECT
b
FROM
cte
WHERE
(ConditionCount > 0 AND MeetsCondition = 1)
OR (ConditionCount = 0)
ORDER BY
b
I find it a bit "ugly". Maybe it would be better to materialize output from your query within a temp table and then based on count from temp table perform first or second query (this limits accessing the original table from 3 times to 2 and you will be able to add some flag for qualifying rows for your condition not to repeat it). Other than that, read below . . .
Though, bear in mind that EXISTS query should execute pretty fast. It stops whether it finds any row that satisfies the condition.
You could achieve this using UNION ALL to combine resultset from constrained query and full query without constraint on a column and then decide what to show depending on output from first query using CASE statement.
How CASE statement works: when any row from constrained part of your query is found, return resultset from constrainted query else return everything omitting the constraint.
If your database supports using CTE use this solution:
with tmp_data as (
select *
from (
select 'constraint' as type, b
from t
where b > a
and a in (1,2,3) -- here goes your constraint
union all
select 'full query' as type, b
from t
where b > a
) foo
)
SELECT b
FROM tmp_data
WHERE
CASE WHEN (select count(*) from tmp_data where type = 'constraint') > 0
THEN type = 'constraint'
ELSE type = 'full query'
END
;
I was wondering if something like this was possible in SQL :
select (
(select count(*) from T) = (select count(*) from T t where t.something = thing)
)
This is probably very far from the actual SQL if it is possible, I don't write database requests so often.
How could I get the result of my comparison with a single request ? Basically, if I had no time, I would just make two requests and compare the results in Java (boooooo !! I know).
Although your query should work, the following is probably faster because only a single query is needed
select total_count = thing_count
from (
select count(*) as total_count,
sum(case when something = 42 then 1 end) as thing_count
from t
) t
The above is ANSI SQL and should work in any DBMS supporting a real boolean type. In Oracle you would need to use an expression in the outer select:
select case when total_count = thing_count then 1 else 0 end
from (
select count(*) as total_count,
sum(case when something = 42 then 1 end) as thing_count
from t
) t
I would write your query like this:
SELECT (CASE WHEN (select count(*) from T) = (select count(*) from T t where t.something = thing) THEN 1 ELSE 0 END)
However, if the first T is the same as the second T then what you actually want to check is if there are any records where t.something <> thing .. right ?
In that case you could simply do :
SELECT (CASE WHEN EXISTS (select * from T t where t.something != thing) THEN 1 ELSE 0 END)
I'm running a netezza sql process as part of a shell script and in one of the sql codes, I want it to raise an ERROR or exception if the number of rows from 2 different tables don't match.
SQL Code:
/* The following 2 tables should return the same number of rows to make sure the process is correct */
select count(*)
from (
select distinct col1, col2,col3
from table_a
where week > 0 and rec >= 1
) as x ;
select count(*)
from (
select distinct col1, col2, col3
from table_b
) as y ;
How do I compare the 2 row counts and raise an exception/ERROR in the netezza SQL process, so that it exits the process, if the 2 row counts aren't equal ?
I agree a script is the best option. However you could still do the check in your SQL itself by using a cross join
Select a.*
from Next_Step_table a cross join
(select case when y.y_cnt is null then 'No Match' else 'Match' end as match
from (select count(*) as x_cnt
from ( select distinct col1, col2,col3
from table_a
where week > 0 and rec >= 1
)) x left outer join
(select count(*) as y_cnt
from (select distinct col1, col2, col3
from table_b
)) y on x.x_cnt=y.y_cnt) match_tbl
where match_tbl.match='Match'
i'm guessing the best solution here is to do it in the script.
i.e store the result of count(*) in variables, then compare them. nzsql has command line options to only return the result data of a single query.
If it must be done in plain SQL, a horribly, horrible kludge that will work is to use divide-by-zero. It's ugly but I've used it before when testing stuff. off the top of my head:
with
subq_x as select count(*) c1 .... ,
subq_y as select count(*) c2 ...
select (case when (subq_x.c1 != subq_y.c1) then 1/0 else 1 end) counts_match;
Did I mention this is ugly ?
I'll try to describe as best I can, but it's hard for me to wrap my whole head around this problem let alone describe it....
I am trying to select multiple results in one query to display the current status of a database. I have the first column as one type of record, and the second column as a sub-category of the first column. The subcategory is then linked to more records underneath that, distinguished by status, forming several more columns. I need to display every main-category/subcategory combination, and then the count of how many of each sub-status there are beneath that subcategory in the subsequent columns. I've got it so that I can display the unique combinations, but I'm not sure how to nest the select statements so that I can select the count of a completely different table from the main query. My problem lies in that to display the main category and sub category, I can pull from one table, but I need to count from a different table. Any ideas on the matter would be greatly appreciated
Here's what I have. The count statements would be replaced with the count of each status:
SELECT wave_num "WAVE NUMBER",
int_tasktype "INT / TaskType",
COUNT (1) total,
COUNT (1) "LOCKED/DISABLED",
COUNT (1) released,
COUNT (1) "PARTIALLY ASSEMBLED",
COUNT (1) assembled
FROM (SELECT DISTINCT
(t.invn_need_type || ' / ' || s.code_desc) int_tasktype,
t.task_genrtn_ref_nbr wave_num
FROM sys_code s, task_hdr t
WHERE t.task_genrtn_ref_nbr IN
(SELECT ship_wave_nbr
FROM ship_wave_parm
WHERE TRUNC (create_date_time) LIKE SYSDATE - 7)
AND s.code_type = '590'
AND s.rec_type = 'S'
AND s.code_id = t.task_type),
ship_wave_parm swp
GROUP BY wave_num, int_tasktype
ORDER BY wave_num
Image here: http://i.imgur.com/JX334.png
Guessing a bit,both regarding your problem and Oracle (which I've - unfortunately - never used), hopefully it will give you some ideas. Sorry for completely messing up the way you write SQL, SELECT ... FROM (SELECT ... WHERE ... IN (SELECT ...)) simply confuses me, so I have to restructure:
with tmp(int_tasktype, wave_num) as
(select distinct (t.invn_need_type || ' / ' || s.code_desc), t.task_genrtn_ref_nbr
from sys_code s
join task_hdr t
on s.code_id = t.task_type
where s.code_type = '590'
and s.rec_type = 'S'
and exists(select 1 from ship_wave_parm p
where t.task_genrtn_ref_nbr = p.ship_wave_nbr
and trunc(p.create_date_time) = sysdate - 7))
select t.wave_num "WAVE NUMBER", t.int_tasktype "INT / TaskType",
count(*) TOTAL,
sum(case when sst.sub_status = 'LOCKED' then 1 end) "LOCKED/DISABLED",
sum(case when sst.sub_status = 'RELEASED' then 1 end) RELEASED,
sum(case when sst.sub_status = 'PARTIAL' then 1 end) "PARTIALLY ASSEMBLED",
sum(case when sst.sub_status = 'ASSEMBLED' then 1 end) ASSEMBLED
from tmp t
join sub_status_table sst
on t.wave_num = sst.wave_num
group by t.wave_num, t.int_tasktype
order by t.wave_num
As you notice, I don't know anything about the table with the substatuses.
You can use inner join, grouping and count to get your result:
suppose tables are as follow :
cat (1)--->(n) subcat (1)----->(n) subcat_detail.
so the query would be :
select cat.title cat_title ,subcat.title subcat_title ,count(*) as cnt from
cat inner join sub_cat on cat.id=subcat.cat_id
inner join subcat_detail on subcat.ID=am.subcat_detail_id
group by cat.title,subcat.title
Generally when you need different counts, you need to use the CASE statment.
select count(*) as total
, case when field1 = "test' then 1 else 0 end as testcount
, case when field2 = 'yes' then 1 else 0 endas field2count
FROM table1
I've a column that have 15 distinct values. I'd like to count how many there are of a few of them,
I've come up with e.g.
select a,COUNT(IFNULL(b != 1,NULL)),COUNT(IFNULL(b != 2,NULL)) from
mytable group by a
select a,SUM(CASE WHEN a = 1 THEN 1 ELSE 0)),SUM(CASE WHEN a = 2 THEN 1 ELSE 0)) from
mytable group by a
What's the best way of doing this ? (note, I need to pivot those values to columns,
a simple select a,b,count(*) from mytable where b=1 or b=2 group by a,b; won't do.)
Of the two methods suggested in the question, I recommend the second:
select a,
SUM(CASE WHEN b = 1 THEN 1 ELSE 0) b1,
SUM(CASE WHEN b = 2 THEN 1 ELSE 0) b2
from mytable
group by a
- as it is both simpler and (I think) easier to understand, and therefore to maintain. I recommend including column aliases, as they make the output easier to understand.
First of all you misunderstood the IFNULL function (you probably wanted IF). See the documentation http://dev.mysql.com/doc/refman/5.0/en/control-flow-functions.html .
The second query you have in your question will give you what you want. But SUM(a=x) is more than sufficient. In MySQL true is equal to 1 and false is equal to 0.
have u try cross join?
select *
from (
select a, sum(...) as aSum
from mytable
where a...
group
by a
) as forA
cross join (
select b, sum(...) as bsum
from (
select *
from mytable
where b...
group
by b
)
) as forB;