Count(*) with 0 for boolean field - sql

Let's say I have a boolean field in a database table and I want to get a tally of how many are 1 and how many are 0. Currently I am doing:
SELECT 'yes' AS result, COUNT( * ) AS num
FROM `table`
WHERE field = 1
UNION
SELECT 'no' AS result, COUNT( * ) AS num
FROM `table`
WHERE field = 0;
Is there an easier way to get the result so that even if there are no false values I will still get:
----------
|yes | 3 |
|no | 0 |
----------

One way would be to outer join onto a lookup table. So, create a lookup table that maps field values to names:
create table field_lookup (
field int,
description varchar(3)
)
and populate it
insert into field_lookup values (0, 'no')
insert into field_lookup values (1, 'yes')
now the next bit depends on your SQL vendor, the following has some Sybase (or SQL Server) specific bits (the outer join syntax and isnull to convert nulls to zero):
select description, isnull(num,0)
from (select field, count(*) num from `table` group by field) d, field_lookup fl
where d.field =* fl.field

you are on the right track, but the first answer will not be correct. Here is a solution that will give you Yes and No even if there is no "No" in the table:
SELECT 'Yes', (SELECT COUNT(*) FROM Tablename WHERE Field <> 0)
UNION ALL
SELECT 'No', (SELECT COUNT(*) FROM tablename WHERE Field = 0)
Be aware that I've checked Yes as <> 0 because some front end systems that uses SQL Server as backend server, uses -1 and 1 as yes.
Regards
Arild

This will result in two columns:
SELECT SUM(field) AS yes, COUNT(*) - SUM(field) AS no FROM table

Because there aren't any existing values for false, if you want to see a summary value for it - you need to LEFT JOIN to a table or derived table/inline view that does. Assuming there's no TYPE_CODES table to lookup the values, use:
SELECT x.desc_value AS result,
COALESCE(COUNT(t.field), 0) AS num
FROM (SELECT 1 AS value, 'yes' AS desc_value
UNION ALL
SELECT 2, 'no') x
LEFT JOIN TABLE t ON t.field = x.value
GROUP BY x.desc_value

SELECT COUNT(*) count, field FROM table GROUP BY field;
Not exactly same output format, but it's the same data you get back.
If one of them has none, you won't get that rows back, but that should be easy enough to check for in your code.

Related

Save value in local variable HANA SQL Script

I'm trying to take value from a non-empty row and overwrite it in the subsequent rows until another non-empty row appears and then write that in the subsequent rows. Coming from ABAP Background, I'm not sure how to accomplish this in HANA SQL Script. Here's a picture to show what the data looks like.
Basically 'Doe, John' should be overwritten into all the empty rows until 'Doe, Jane' appears and then 'Doe, Jane' should be overwritten into empty rows until another name appears.
My idea is to store the non-empty row in a local variable, but I haven't had much success so far. Here's my code:
tempTab1 = SELECT
CASE WHEN EMPLOYEE <> ''
THEN lv_emp = EMPLOYEE
ELSE EMPLOYEE
END AS EMPLOYEE,
FROM :tempTab;
In general, rows in dataset are unordered until you explicitly specify ORDER BY part of SQL. If you observe some order it may be a side-effect and can vary. So first of all you have to explicitly create a row number column (assume it's name is RECORD).
Then you should go this way:
Select only rows with non-empty data in column.
Use LEAD(RECORD) over(order by RECORD) to identify the next non-empty record number.
Join your source dataset to dataset defined on step 3 on between condition for RECORD field.
with a as (
select 1 as record, 'Val1' as field1 from dummy union
select 2 as record, '' as field1 from dummy union
select 3 as record, '' as field1 from dummy union
select 4 as record, 'Val2' as field1 from dummy union
select 5 as record, '' as field1 from dummy union
select 6 as record, '' from dummy union
select 7 as record, '' from dummy union
select 8 as record, 'Val3' as field1 from dummy
)
, fill_base as (
select field1, record, lead(record, 1, record) over(order by record asc) as next_record
from a
where field1 <> '' and field1 is not null
)
select
a.record
, case
when a.field1 = '' or a.field1 is null
then f.field1
else a.field1
end as field1
, a.field1 as field1_original
from a
left join fill_base as f
on a.record > f.record
and a.record < f.next_record
The performance in HANA may be bad in some cases since it process window functions very bad.
Here is another more elegant solution with two nested window functions than does not force you to write multiple selects for each column: How to make LAG() ignore NULLS in SQL Server?
You can use window aggregate function LAST_VALUE to achieve the imputation of missing values.
Sample Data
CREATE TABLE sample (id integer, sort integer, value varchar(10));
INSERT INTO sample VALUES (4711, 1, 'Hello');
INSERT INTO sample VALUES (4712, 2, null);
INSERT INTO sample VALUES (4713, 3, null);
INSERT INTO sample VALUES (4714, 4, 'World');
INSERT INTO sample VALUES (4715, 5, null);
INSERT INTO sample VALUES (4716, 6, '!');
Generate a new column with imputed values
SELECT base.*, LAST_VALUE(fill.value ORDER BY fill.sort) AS value_imputed
FROM sample base
LEFT JOIN sample fill ON fill.sort <= base.sort AND fill.value IS NOT NULL
GROUP BY base.id, base.sort, base.value
ORDER BY base.id, base.sort
Result
Note that sort could be anything determining the order (e.g. a timestamp).

Nested query that requires the first result to be returned

I have 2 tables as such
Table ErrorCodes:
type_code desc
01 Error101
02 Error99
03 Error120
Table ErrorXML:
row_index typeCode
1 87
2 02
3 01
The output should be the description(column desc) of the first matched type_code between the 2 tables
Expected output : Error99
I have gotten so far.
select isnull(descript, 'unknown') as DESCRIPTION
from (select top 1 a.stmt_cd as descript
from ErrorCodes a, ErrorXML b
where a.type_cd = b.typecode
order by b.row_index)
But this query doesn't return the string UNKNOWN when there is no common typecode (join condition) between the 2 tables. In this case, im getting null.
How can I resolve this?
This is an interesting question. I believe the following can be an intuitive and beautiful solution (I used desc_ as column name rather than desc which is a reserved word):
select (select desc_ from ErrorCodes x where x.type_code = a.typeCode) desc_
from ErrorXML a
where (select desc_ from ErrorCodes x where x.type_code = a.typeCode) is not null
order by row_index
limit 1;
If you also need to handle the case if query returns no row then for MySQL, following syntax should suffice. For other databases you can use similar encapsulation with isnull, nvl, etc:
select ifnull((select (select desc_ from ErrorCodes x where x.type_code = a.typeCode) desc_ from ErrorXML a where (select desc_ from ErrorCodes x where x.type_code = a.typeCode) is not null order by row_index limit 1), 'UNKNOWN');
To test I used following scripts and seems to work properly:
create database if not exists stackoverflow;
use stackoverflow;
drop table if exists ErrorCodes;
create table ErrorCodes
(
type_code varchar(2),
desc_ varchar(10)
);
insert into ErrorCodes(type_code, desc_) values
('01', 'Error101'),
('02', 'Error99'),
('03', 'Error120');
drop table if exists ErrorXML;
create table ErrorXML
(
row_index integer,
typeCode varchar(2)
);
insert into ErrorXML(row_index, typeCode) values
('1', '87'),
('2', '02'),
('3', '01');
Final-1 quote: While generating your tables try to use same column names as much as possible. I.e. I'd suggest ErrorXML to use type_code rather than typeCode.
Final quote: I choose to use lower letters in SQL since capital letters should be used while emphasizing an important point. I also suggest that style.
What about this: Do a subquery to bring back the first row_index for each type_code.
Do a LEFT OUTER Join on the ErrorCodes table so that you get NULLs as well.
SELECT
ISNULL(ErrorCodes.desc,'unknown') AS description
ErrorXML.row_index
FROM ErrorCodes
LEFT OUTER JOIN (
SELECT type_code, MIN(row_index) AS row_index
FROM ErrorXML
GROUP BY type_code
) AS ErrorXML ON ErrorCodes.type_code = ErrorXML .type_code

Returning distinct prioritizing results with order by

Name varchar, Value int, Active bit
-----------------------------------
'Name1',1,1
'Name2',2,1
'Name1',3,0
'Name2',4,0
'Name3',1,1
'Name4',1,1
I want to return where Active is anything but prioritize when it's 0 so I want to return this:
'Name1',3
'Name2',4
'Name3',1
'Name4',1
I tried this, but get an error to include Active in my return statement
Select Distinct Name, Value From Table Order by Active
So I tried this:
Select Distinct Name, Value, Active From Table Order by Active
But now it returns all the rows. I would like to prioritize where Active = 0 in the distinct results but since it requires I put Active in the return statement makes this complicated.
Can someone help?
Your question is a little confusing, but if I'm understanding it correctly, you need to use a group by statement:
select name,
max(case when active = 0 then value end) value
from yourtable
group by name
SQL Fiddle Demo
With your edits, you can use coalesce and still get it to work:
select name, coalesce(max(case when active = 0 then value end), max(value)) value
from yourtable
group by name
More Fiddle
You can order by fields not contained in the select clause
Select Name, Value
From Table
ORDER BY Active, Name, Value
But you cannot use SELECT DISTINCT at the same time.
If you use "select distinct" there is the possibility that some rows will be discarded, when this happens there is no longer any viable relationship retained between [Active] and the "distinct" rows. So if using select distinct, and you need to order by [Active], then [Active] MUST be in the select clause.
I couldn't delete the post b/c of the other answers, but here is answer I was looking for in case anyone else was wondering.
SELECT Distinct Name,Value FROM Table WHERE Active = 0
UNION ALL
SELECT Distinct Name,Value FROM Table a WHERE Active = 1 AND NOT EXISTS (
SELECT TOP 1 1 FROM Table a2 WHERE a2.Active = 0 AND a2.Name = a.Name
)
Review #Sgeddes 's answer for a better solution.
Thanks to everyone for their help.
Perhaps this:
create table #t(
Active int not null,
Name varchar(10) not null,
Value int not null,
primary key clustered (Active desc,Name,Value)
);
insert #t(Active,Name,Value)
select Active,Name,Value from [Table];
select Name, Value
from #t;
go
yields as desired:
Name Value
---------- -----------
Name1 1
Name2 2
Name3 1
Name4 1
Name1 3
Name2 4

Returning rows that had no matches

I've read and read and read but I haven't found a solution to my problem.
I'm doing something like:
SELECT a
FROM t1
WHERE t1.b IN (<external list of values>)
There are other conditions of course but this is the jist of it.
My question is: is there a way to show which in the manually entered list of values didn't find a match? I've looked but I can't find and I'm going in circles.
Create a temp table with the external list of values, then you can do:
select item
from tmptable t
where t.item not in ( select b from t1 )
If the list is short enough, you can do something like:
with t as (
select case when t.b1='FIRSTITEM' then 1 else 0 end firstfound
case when t.b1='2NDITEM' then 1 else 0 end secondfound
case when t.b1='3RDITEM' then 1 else 0 end thirdfound
...
from t1 wher t1.b in 'LIST...'
)
select sum(firstfound), sum(secondfound), sum(thirdfound), ...
from t
But with proper rights, I would use Nicholas' answer.
To display which values in the list of values haven't found a match, as one of the approaches, you could create a nested table SQL(schema object) data type:
-- assuming that the values in the list
-- are of number datatype
create type T_NumList as table of number;
and use it as follows:
-- sample of data. generates numbers from 1 to 11
SQL> with t1(col) as(
2 select level
3 from dual
4 connect by level <= 11
5 )
6 select s.column_value as without_match
7 from table(t_NumList(1, 2, 15, 50, 23)) s -- here goes your list of values
8 left join t1 t
9 on (s.column_value = t.col)
10 where t.col is null
11 ;
Result:
WITHOUT_MATCH
-------------
15
50
23
SQLFiddle Demo
There is no easy way to convert "a externally provided" list into a table that can be used to do the comparison. One way is to use one of the (undocumented) system types to generate a table on the fly based on the values supplied:
with value_list (id) as (
select column_value
from table(sys.odcinumberlist (1, 2, 3)) -- this is the list of values
)
select l.id as missing_id
from value_list l
left join t1 on t1.id = l.id
where t1.id is null;
There are ways to get what you have described, but they have requirements which exceed the statement of the problem. From the minimal description provided, there's no way to have the SQL return the list of the manually-entered values that did not match.
For example, if it's possible to insert the manually-entered values into a separate table - let's call it matchtbl, with the column named b - then the following should do the job:
SELECT matchtbl.b
FROM matchtbl
WHERE matchtbl.b NOT IN (SELECT distinct b
FROM t1)
Of course, if the data is being processed by a programming language, it should be relatively easy to keep track of the set of values returned by the original query, by adding the b column to the output, and then perform the set difference.
Putting the list in an in clause makes this hard. If you can put the list in a table, then the following works:
with list as (
select val1 as value from dual union all
select val2 from dual union all
. . .
select valn
)
select list.value, count(t1.b)
from list left outer join
t1
on t1.b = list.value
group by list.value;

Oracle SQL: Returning a Record even when a specific value doesn't exist

I have a query where I'm trying to pull some values from a table where a specific ID is queried for. If that value doesn't exist, I would still like the query to return a record that only has that ID value I was looking for. Here's what I've tried so far.
Select attr.attrval, attr.uidservicepoint, sp.servicepointid
From bilik.lssrvcptmarketattr attr
Join bilik.lsmarketattrtype type on attr.uidmarketattrtype = type.uidmarketattrtype AND
type.attrtype IN ('CAPACITY_REQUIREMENT_KW') and TO_CHAR( attr.starttime , 'mm/dd/yyyy')in ('05/01/2011')
Right Outer Join bilik.lsservicepoint sp on attr.uidservicepoint = sp.uidservicepoint
Where sp.servicepointid in ('RGE_R01000051574382') Order By sp.servicepointid ASC
In this example, I'm trying to look for RGE_R01000051574382. If that doesn't exist in table SP.servicepointid, I want it to still return the 'RGE_R01000051574382' in a record with nulls for the other values I'm pulling. Normally, when I'm running this, I will be pulling about 1000 specific values at a time.
If anyone has any insight that they can give on this, it would be greatly appreciated. Thanks so much!
If I understand correctly, you just need to move the WHERE clause into the JOIN clause.
select attr.attrval,
attr.uidservicepoint,
sp.servicepointid
from bilik.lssrvcptmarketattr attr
join bilik.lsmarketattrtype type on attr.uidmarketattrtype = type.uidmarketattrtype
and type.attrtype in ('CAPACITY_REQUIREMENT_KW')
and TO_CHAR(attr.starttime, 'mm/dd/yyyy') in ('05/01/2011')
right outer join bilik.lsservicepoint sp on attr.uidservicepoint = sp.uidservicepoint
and sp.servicepointid in ('RGE_R01000051574382')
order by sp.servicepointid
I think you're saying you want to have a record returned, with the servicepointid column populated, but all others null?
In that case, use a union.
select ...your query without order by...
and sp.servicepointid = 'RGE_R010000515743282'
union
select null, null, 'RGE_R010000515743282'
from dual
where not exists (select 'x' from (...your query without order by...))
Here's a complete example:
create table test (id number, val varchar2(10));
insert into test (id, val) values (1, 'hi');
select id,
val
from test
where id = 1
union
select 1,
null
from dual
where not exists (select 'x'
from test
where id = 1)