UPDATE psql table with calculated value - sql

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.

Related

how can I add a new column where there may be matches on an id field

I currently have a table that contains an id, and a count of a criteria for that id field. For example my table looks like this:
ID Banana_count
1 13
2 23
3 56
The original counts came from a join and a query from other tables.
create FRUIT_TABLE as
select id, count (fruit)
from my_table a
where exists (select null from DATE_FED b
where a.id = b.id
and date = (2/11/17)
and fruit_type = 'banana')
group by id;
My question is, how can i add other attributes to this particular table so that it looks like:
ID Banana_count Apple_count Orange_count
1 13 35 22
2 23 44
3 56
4 33 55
5 11
I will have to add more ids to FRUIT_TABLE that may not already be in the current table, but for fruits that are currently associated with an id, i'd like to add them in the same row.
This is a classic use case for merge:
merge into fruit_table
using apple_table
on (fruit_table.id = apple_table.id)
when matched then update set
fruit_table.apples = apple_table.apples
when not matched then insert (id,apples)
values(
apple_table.id,
apple_table.apples
);
I have simplified the problem slightly so that you are inserting from a table that simply has ids and a count of apples, so that the structure of the merge is clearer. But you can insert a subquery instead into the using... section of the statement to meet your actual requirements.
I would look into something like the following [you didn't provide your table definitions, or other application or requirements constraints so an exact answer is not possible]:
create FRUIT_TABLE as
select id
, sum(case when fruit_type = 'banana' then 1 else 0 end ) Banana_count
, sum(case when fruit_type = 'apple' then 1 else 0 end ) apple_count
, sum(case when fruit_type = 'orange' then 1 else 0 end ) orange_count
from my_table a
group by id;

grouping with oracle with clause

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

SQL (TSQL) - Select values in a column where another column is not null?

I will keep this simple- I would like to know if there is a good way to select all the values in a column when it never has a null in another column. For example.
A B
----- -----
1 7
2 7
NULL 7
4 9
1 9
2 9
From the above set I would just want 9 from B and not 7 because 7 has a NULL in A. Obviously I could wrap this as a subquery and USE the IN clause etc. but this is already part of a pretty unique set and am looking to keep this efficient.
I should note that for my purposes this would only be a one-way comparison... I would only be returning values in B and examining A.
I imagine there is an easy way to do this that I am missing, but being in the thick of things I don't see it right now.
You can do something like this:
select *
from t
where t.b not in (select b from t where a is null);
If you want only distinct b values, then you can do:
select b
from t
group by b
having sum(case when a is null then 1 else 0 end) = 0;
And, finally, you could use window functions:
select a, b
from (select t.*,
sum(case when a is null then 1 else 0 end) over (partition by b) as NullCnt
from t
) t
where NullCnt = 0;
The query below will only output one column in the final result. The records are grouped by column B and test if the record is null or not. When the record is null, the value for the group will increment each time by 1. The HAVING clause filters only the group which has a value of 0.
SELECT B
FROM TableName
GROUP BY B
HAVING SUM(CASE WHEN A IS NULL THEN 1 ELSE 0 END) = 0
If you want to get all the rows from the records, you can use join.
SELECT a.*
FROM TableName a
INNER JOIN
(
SELECT B
FROM TableName
GROUP BY B
HAVING SUM(CASE WHEN A IS NULL THEN 1 ELSE 0 END) = 0
) b ON a.b = b.b

Calculate percentage for each value of a column sql

I want to rewrite this sql query so that he shows a record with 0 for the corresponding age range if there are no matches and I want that he counts the percentages for each value of Member instead of the '0' at this moment, can anyone help me how I can achieve this?
SELECT COUNT(Name) * 100 /
(select COUNT(*) from 'cities'
WHERE city= 'Hoeselt' AND Member = '0' ) AS 'perc',
CASE
WHEN age <= 30 THEN '18-30'
WHEN age <= 50 THEN '31-50'
ELSE '50+'
END AS age, COUNT(*) AS n
FROM 'cities'
WHERE city= 'Hoeselt' AND elected='yes' AND Member= '0'
GROUP BY CASE
WHEN age <= 30 THEN '18-30'
WHEN age <= 50 THEN '31-50'
ELSE '50+'
END
Hard to be certain that this will work for you without the DDL.
This is a great tool for helping people give you the best solution.
http://sqlfiddle.com/#!6
;WITH AgeCat AS
(
SELECT MinAge = 18
,MaxAge = 30
,Descr = '18-30' UNION ALL
SELECT 31, 49, '31-49' UNION ALL
SELECT 50, 200, '50+'
)
SELECT DISTINCT
C.Descr
,Perc = COUNT(*) OVER (PARTITION BY 0) / COUNT(*) OVER (PARTITION BY A.Descr) * 100
FROM AgeCat A
JOIN Cities C ON C.Age BETWEEN A.MinAge AND A.MaxAge
WHERE city = 'Hoeselt'
AND elected = 'yes'
AND Member = '0'
My approach to this is to use a CTE to define the age group. Next select all the age groups as a "driver" table, left joining in the cities information. Then, you have the age group even when there are no matches:
with c as (
select c.*,
(CASE WHEN age <= 30 THEN '18-30'
WHEN age <= 50 THEN '31-50'
ELSE '50+'
END) as agegrp
from cities
)
select COUNT(Name) * 100 / (select COUNT(*) from cities WHERE city= 'Hoeselt' AND Member = '0') as perc,
driver.agegrp,
COUNT(*) as n
from (select distinct agegrp from c) as driver left outer join
c
on driver.agegrp = c.agegrp
group by driver.agegrp

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
;