grouping with oracle with clause - sql

i have a table from which i am trying to pull the frequency distribution by age-group and score-group. Below is the query I am running.
with age_map as (select distinct age,case when age is not null
and AGE>=16 AND AGE<36 then '16-35'
when age is not null and AGE>=36 AND
AGE<56 then '36-56'
when age is not null and AGE>=56 then
'56+'
when age is null then 'NA'
end as age_group
from rec_table
where monthofsale = 'Apr 2017'
)
select name,location,b.age_group,sum(weight),count(*)
from rec_table a, age_map b
where a.age = b.age
group by name,location,b.age_group
When running the query, I keep getting the error:
ORA-00979: not a GROUP BY expression
I am pretty sure I am including all the columns. So, wondering if this is not correct?
My expected output is:
Name location age_group weight count
x y 16-35 15 3
p q 36-56 48 7
Any ideas on this?

The group by error is coming from the with clause:
Do the select like this:
select age, case when age is not null and AGE>=16 AND AGE<36 then '16-35'
when age is not null and AGE>=36 AND AGE<56 then '36-56'
when age is not null and AGE>=56 then '56+'
when age is null then 'NA'
end as age_group
from rec_table
where monthofsale = 'Apr 2017'
group by age, case when age is not null and AGE>=16 AND AGE<36 then '16-35'
when age is not null and AGE>=36 AND AGE<56 then '36-56'
when age is not null and AGE>=56 then '56+'
when age is null then 'NA'
end

Related

How to combine 2 SQL queries of same table into as separate columns

If I want to show the total number of male patients and the total number of female patients in the patients table having gender as a column as:
male_count female_count
1220 1105
How do I do this ?
structure of patients table (patient_id INT, first_name CHAR, last_name CHAR, city CHAR, birth_date DATE and gender CHAR)
Entries in gender column include 'M' and 'F'
I am trying to learn SQL. Please revert with a possible solution
You can use subqueries like this
SELECT
(SELECT count(*) FROM patients WHERE gender = 'F') as female_count,
(SELECT count(*) FROM patients WHERE gender = 'M') as male_count
;
Not the prettiest way to do it, but should work
You can use CASE statements and SUM the results for each column.
SELECT
SUM(CASE WHEN gender = 'M' THEN 1 ELSE 0 END) AS male_count,
SUM(CASE WHEN gender = 'F' THEN 1 ELSE 0 END) AS female_count
FROM patients;

SQL works in Athena Engine v1 but not v2

I have a SQL query embedded into a system that has worked successfully until now in Athena with engine version 1. However it fails in engine version 2 and I haven't been able to work out why.
Here is a generalised version of the SQL. It sums the number of people in 3 groups: adults, NY residents and the overlap of the two. (NY adults).
In version 1 this works, but in v2 I get the error "column z.id_field cannot be resolved"
WITH BASE AS (SELECT person_id, age, state
FROM people
WHERE gender = 'male'
)
,group_a as (
SELECT distinct (person_id) as id_field
FROM BASE
WHERE age > 17
),
group_b as (
SELECT distinct (person_id) as id_field
FROM BASE
WHERE state = 'NY'
)
SELECT CASE WHEN z.id_field is null then 'group_b_only' WHEN r.id_field is null then 'group_a_only' ELSE 'Overlap' END as group
, COUNT (coalesce (z.id_field, r.id_field)) as count
FROM group_a AS z FULL OUTER JOIN group_b as r USING (id_field)
GROUP BY 1;
As a note, in any database this would be simpler as an aggregation and probably faster too:
SELECT grp, COUNT(*)
FROM (SELECT person_id,
(CASE WHEN MAX(age) > 17 AND MAX(state) = 'NY' THEN 'Both'
WHEN MAX(age) > 17 THEN 'Age Only'
ELSE 'State Only'
END) as grp
FROM people
WHERE gender = 'male' AND
(age > 17 OR state = 'NY')
GROUP BY person_id
) x
GROUP BY grp;
The above assumes that person_id can be repeated in people. If that is not the case, then this can be simplified to:
SELECT (CASE WHEN age > 17 AND state = 'NY' THEN 'Both'
WHEN age > 17 THEN 'Age Only'
ELSE 'State Only'
END) as grp, COUNT(*)
FROM people
WHERE gender = 'male' AND
(age > 17 OR state = 'NY')
GROUP BY grp;

UPDATE psql table with calculated value

Table A:
id | dob
Table B:
id | type
I want to calculate age from A.dob and based on that I want to update B.type, I tried following but it is giving me an error.
UPDATE B
SET B.type = CASE
WHEN AGE <= 16 THEN 'C'
WHEN AGE>25 and age<=40 THEN 'Y'
WHEN AGE>40 THEN 'O'
END
from AGE as ( EXTRACT(YEAR FROM age(now(),A.dob)) ), A inner join B on A.id=B.id
where A.dob is not null;
Try something like this:
update b
set type = (case when age <= 16 then 'C'
when age > 25 and age <= 40 then 'Y'
when age > 40 then 'O'
end)
from (select a.*, extact(year from age(now(), a.dob)) as age
from a
) a
where a.id = b.id and a.age is not null;
Notes:
Your logic has no type for 17 to 24 year-olds.
Don't repeat the table being updated in the FROM clause. That is not how Postgres works.
The JOIN conditions -- alas -- go in the WHERE clause.
This uses a subquery to calculate age.
I figure you might as well test for age not being NULL rather than the base column.

I want to return a default value as NULL for some condition in SQL Server

I am running the below SQL query in SQL Server where I need to return a default value as ’NULL’ when a condition is not met. The result should be displayed as NULL for some records which doesn’t meet the criteria but I am getting a blank space in-place of NULL.Can someone please help?
There are few rows in my table which doesn’t meet the criteria so I should be seeing NULL in my result.
SELECT NAME,
CASE
WHEN AGE >=18 AND SEX='M' THEN 'Adult Male'
WHEN AGE>18 AND SEX='M' 'Non-Adult Male'
WHEN AGE >=18 AND SEX='F' THEN 'Adult Female'
WHEN AGE<18 AND TITLE='F' THEN 'Non-Adult Female'
ELSE NULL
END AS AGE_SEX,
ADDRESS,
SALARY
FROM PERSONALS;
Your logic is not correct. Here is a way to fix it, taking into account that case statements cascade -- that is, the logic chooses the first match:
SELECT NAME,
(CASE WHEN AGE >= 18 AND SEX = 'M' THEN 'Adult Male'
WHEN AGE < 18 AND SEX = 'M' THEN 'Non-Adult Male'
WHEN AGE >= 18 AND SEX = 'F' THEN 'Adult Female'
WHEN AGE < 18 AND SEX = 'F' THEN 'Non-Adult Female'
END) AS AGE_SEX
Note: The ELSE NULL is redundant, because the CASE returns NULL when no conditions match.

SQL query to (group by ) by condition of column

if I want to make a query that gets the count of users grouping ages
to get the counts each year as alone :
select count(*)
from tbl_user
group by age
how can I make a custom group by so I can get ages in ranges for example ...
like this example :
group by ages as 0-18 , 19-25 , 26-...
Use a CASE expression in a subquery and group by that expression in the outer query:
select age_group, count(*)
from (
select case when age between 0 and 18 then '0-18'
when age between 19 and 26 then '19-25'
...
end as age_group
from tbl_user
) t
group by age_group
SUM 1 and CASE WHEN work in MS SQL Server, which version of SQL are you using?
SELECT
SUM(CASE WHEN Age >= 0 AND Age <= 18 THEN 1 ELSE 0 END) AS [0-18],
SUM(CASE WHEN Age >= 19 AND Age <= 25 THEN 1 ELSE 0 END) AS [19-25]
FROM
YourTable
You could use a CASE statement:
SELECT Sum(CASE WHEN age BETWEEN 0 AND 18 THEN 1 ELSE 0 END) as [0-18],
Sum(CASE WHEN age BETWEEN 19 AND 25 THEN 1 ELSE 0 END) as [19-25],
Sum(CASE WHEN age BETWEEN 26 AND 34 THEN 1 ELSE 0 END) as [26-34]
FROM tbl_user
this will "flatten" the data into one row - to get one row per grouping use this as the basis for a View, then select from that.
Data belongs in a table, not in the code. The age categories are data, IMHO.
CREATE TABLE one
( val SERIAL NOT NULL PRIMARY KEY
, age INTEGER NOT NULL
);
INSERT INTO one (age) SELECT generate_series(0,31, 1);
CREATE TABLE age_category
( low INTEGER NOT NULL PRIMARY KEY
, high INTEGER NOT NULL
, description varchar
);
INSERT INTO age_category (low,high,description) VALUES
( 0,19, '0-18')
, ( 19,26, '19-25')
, ( 26,1111, '26-...')
;
SELECT ac.description, COUNT(*)
FROM one o
JOIN age_category ac ON o.age >= ac.low AND o.age < ac.high
GROUP BY ac.description
;