Count values in Oracle table - sql

I have this table which I want to use to store events.
CREATE TABLE EVENTS(
EVENTID INTEGER NOT NULL,
SOURCE VARCHAR2(50 ),
TYPE VARCHAR2(50 ),
EVENT_DATE DATE,
DESCRIPTION VARCHAR2(100 )
)
T have four types of events: info, warning, error, Critical
I need to count them in order to display the values into Bar Chart.
Is it possible to create SQL query which returns four values. For example:
info 12,
warning 332,
error 442,
Critical 23
I need only the type and the count.

It looks like you want a simple aggregation
SELECT type, count(*)
FROM events
GROUP BY type
ORDER BY (CASE type WHEN 'info' THEN 1
WHEN 'warning' THEN 2
WHEN 'error' THEN 3
WHEN 'critical' THEN 4
END) asc
It's not obvious to me whether (or how) you are sorting the data. I would expect that you'd want to store a sort order somewhere so that you don't have dozens of queries that implement the same sort order that have to be changed in the future when you add another type.

You can see GROUP BY reference for aditional information
SELECT type, count(*)
FROM events
GROUP BY type

You can get the desired output in these two ways:
SELECT type,COUNT(*) FROM EVENTS GROUP BY type;
SELECT DISTINCT type, COUNT(*) over(partition BY type) FROM EVENTS;

Related

Design select SQL query

I have three values expected in a table case, Serious, Non-Serious, Unknown for each case_id
select case_id, case_seriousness
from case;
I have to build a SQL query which should show one row per case_id.
If there are rows for a case_id with multiple values, then only one row should appear based on priority - Serious, Non-Serious then Unknown.
e.g. Serious is in one row rest of four rows have Non-Serious or Unknown then Serious will be he value to show in one record.
If there are records with Non-serious and Unknown then Non-Serious should appear.
So Priorities will be like from S, NS and UK
You can use the analytical function as follows:
select case_id, case_seriousness
from
(select case_id, case_seriousness,
row_number() over (partition by case_id
order by case case_seriousness
when 'Serious' then 1
when 'Non-Serious' then 2
else 3
end ) as rn
from case)
where rn = 1;
Alternatively, You can also use DECODE instead of CASE..WHEN

SQL query to get conflicting values in JSONB from a group

I have a table defined similar to the one below. location_id is a FK to another table. Reports are saved in an N+1 fashion: for a single location, N reporters are available, and there's one report used as the source of truth, if you will. Reports from reporters have a single-letter code (let's say R), the source of truth has a different code (let's say T). The keys for the JSONB column are regular strings, values are any combination of strings, integers and integral arrays.
create table report (
id integer not null primary key,
location_id integer not null,
report_type char(1),
data jsonb
)
Given all the information above, how can I get all location IDs where the data values for a given set of keys (supplied at query time) are not all the same for the report_type R?
There are at least two solid approaches, depending on how complex you want to get and how numerous and/or dynamic the keys are. The first is very straightforward:
select location_id
from report
where report_type = 'R'
group by location_id
having count(distinct data->'key1') > 1
or count(distinct data->'key2') > 1
or count(distinct data->'key3') > 1
The second construction is more complex, but has the advantage of providing a very simple list of keys:
--note that we also need distinct on location id to return one row per location
select distinct on(location_id) location_id
--jsonb_each returns the key, value pairs with value in type JSON (or JSONB) so the value field can handle integers, text, arrays, etc
from report, jsonb_each(data)
where report_type = 'R'
and key in('key1', 'key2', 'key3')
group by location_id, key
having count(distinct value) > 1
order by location_id

Determine the number of times a null value occurs in column B for a distinct value in column A, SQL table

I have a SQL table with "name" as one column, date as another, and location as a third. The location column supports null values.
I am trying to write a query to determine the number of times a null value occurs in the location column for each distinct value in the name column.
Can someone please assist?
One method uses conditional aggregation:
select name, sum(case when location is null then 1 else 0 end)
from t
group by name;
Another method that involves slightly less typing is:
select name, count(*) - count(location)
from t
group by name;
use count along with filters, as you only requires Null occurrence
select name, count(*) occurances
from mytable
where location is null
group by name
From your question, you'll want to get a distinct list of all different 'name' rows, and then you would like a count of how many NULLs there are per each name.
The following will achieve this:
SELECT name, count(*) as null_counts
FROM table
WHERE location IS NULL
GROUP BY name
The WHERE clause will only retrieve records where the records have NULL as their location.
The GROUP BY will pivot the data based on NAME.
The SELECT will give you the name, and the COUNT(*) of the number of records, per name.

Displaying entire row of max() value from nested table

My table CUSTOMER_TABLE has a nested table of references toward ACCOUNT_TABLE. Each account in ACCOUNT_TABLE has a reference toward a branch: branch_ref.
CREATE TYPE account AS object(
accid integer,
acctype varchar2(15),
balance number,
rate number,
overdraft_limit integer,
branch_ref ref branch,
opendate date
) final;
CREATE TYPE customer as object(
custid integer,
infos ref type_person,
accounts accounts_list
);
create type branch under elementary_infos(
bid integer
) final;
All tables are inherited from these object types.
I want to select the account with the highest balance per branch. I arrived to do that with this query:
select MAX(value(a).balance), value(a).branch_ref.bid
from customer_table c, table(c.accounts) a
group by value(a).branch_ref.bid
order by value(a).branch_ref.bid;
Which returns:
MAX(VALUE(A).BALANCE) VALUE(A).BRANCH_REF.BID
--------------------------------------- ---------------------------------------
176318.88 0
192678.14 1
190488.19 2
196433.93 3
182909.84 4
However, how to select as well others attribues from the max accounts displayed ? I would like to display the name of the owner plus the customer's id. The id is directly an attribute of customer. But the name is stored with a reference toward person_table. So I have to select as well c.id & deref(c.infos).names.surname.
How to select these other attributes with my MAX() query ?
Thank you
I generally use analytic functions to achieve that kind of functionality. With analytic functions, you can add aggregate columns to your query without losing the original rows. In your particular case it would be something like:
select
-- select interesting fields
accid,
acctype,
balance,
rate,
overdraft_limit,
branch_ref,
opendate,
max_balance
from (
select
-- propagate original fields to outer query
value(a).accid accid,
value(a).acctype acctype,
value(a).balance balance,
value(a).rate rate,
value(a).overdraft_limit overdraft_limit,
value(a).branch_ref branch_ref,
value(a).opendate opendate,
-- add max(balance) of our branch_ref to the row
max(value(a).balance) over (partition by value(a).branch_ref.bid) max_balance
from customer_table c, table(c.accounts) a
) data
where
-- we are only interested in rows with balance equal to the max
-- (NOTE: there might be more than one, you should fine tune the filtering!)
data.balance = data.max_balance
-- order by branch
order by data.branch_ref.bid;
I don't have any Oracle instance available right now to test this, but that would be the idea, unless there is some kind of incompatibility between analytic functions and collection columns, you should be able to have that working with little effort.

Getting the min() of a count(*) column

I have a table called Vehicle_Location containing the columns (and more):
ID NUMBER(10)
SEQUENCE_NUMBER NUMBER(10)
TIME DATE
and I'm trying to get the min/max/avg number of records per day per id.
So far, I have
select id, to_char(time), count(*) as c
from vehicle_location
group by id, to_char(time), min having id = 16
which gives me:
ID TO_CHAR(TIME) COUNT(*)
---------------------- ------------- ----------------------
16 11-05-31 159
16 11-05-23 127
16 11-06-03 56
So I'd like to get the min/max/avg of the count(*) column. I am using Oracle as my RDBMS.
I don't have an oracle station to test on but you should be able to just wrap the aggregator around your SELECT as a subquery/derived table/inline view
So it would be (UNTESTED!!)
SELECT
AVG(s.c)
, MIN(s.c)
, MAX(s.c)
, s.ID
FROM
--Note this is just your query
(select id, to_char(time), count(*) as c from vehicle_location group by id, to_char(time), min having id = 16) as s
GROUP BY s.ID
Here's some reading on it:
http://www.devshed.com/c/a/Oracle/Inserting-SubQueries-in-SELECT-Statements-in-Oracle/3/
EDIT: Though normally it is a bad idea to select both the MIN and MAX in a single query.
EDIT2: The min/max issue is related to how some RDBMS (including oracle) handle aggregations on indexed columns. It may not affect this particular query but the premise is that it's easy to use the index to find either the MIN or the MAX but not both at the same time because any index may not be used effectively.
Here's some reading on it:
http://momendba.blogspot.com/2008/07/min-and-max-functions-in-single-query.html