I have a table with POI, you can have multiple POI on each city.
SQL DEMO
CREATE TABLE POI
("poi_id" int GENERATED BY DEFAULT AS IDENTITY,
"city_id" int,
PRIMARY KEY("poi_id")
);
Now there were some changes on the cities polygons and now have to reassign some POI
CREATE TABLE newCities
("city_id" int, "new_city_id" int)
;
DATA
INSERT ALL
INTO POI ("poi_id", "city_id")
VALUES (10, 1)
INTO POI ("poi_id", "city_id")
VALUES (11, 1)
INTO POI ("poi_id", "city_id")
VALUES (12, 2)
INTO POI ("poi_id", "city_id")
VALUES (13, 2)
INTO POI ("poi_id", "city_id")
VALUES (14, 5)
SELECT * FROM dual
;
INSERT ALL
INTO newCities ("city_id", "new_city_id")
VALUES (1, 100)
INTO newCities ("city_id", "new_city_id")
VALUES (2, 200)
INTO newCities ("city_id", "new_city_id")
VALUES (3, 200)
SELECT * FROM dual
;
When I do a JOIN:
SELECT *
FROM poi p
JOIN newCities nc
ON p."city_id" = nc."city_id";
OUTPUT
+--------+---------+---------+-------------+
| poi_id | city_id | city_id | new_city_id |
+--------+---------+---------+-------------+
| 10 | 1 | 1 | 100 |
| 11 | 1 | 1 | 100 |
| 12 | 2 | 2 | 200 |
| 13 | 2 | 2 | 200 |
+--------+---------+---------+-------------+
But when I try to do the update but got the error:
ORA-01779: cannot modify a column which maps to a non key-preserved table
UPDATE (
SELECT p.*, nc."new_city_id"
FROM poi p
JOIN newCities nc
ON p."city_id" = nc."city_id"
) t
SET t."city_id" = t."new_city_id";
I know city_id isn't a PK but the row is match with a row including a PK. So why isn't working?
I know I can do a sub query to get the value:
UPDATE poi p
SET "city_id" = COALESCE((SELECT "new_city_id"
FROM newCities c
WHERE c."city_id" = p."city_id")
, p."city_id");
But still want to know what cases the UPDATE JOIN would work because looks like only can work to update the PK:
After doing the sample test decide try creating a PK on the second table and works:
SQL DEMO
CREATE TABLE newCities
("city_id" int GENERATED BY DEFAULT AS IDENTITY,
"new_city_id" int,
PRIMARY KEY("city_id")
);
Related
I'm new to HQL. I need to fetch all the records from table A based on the following 2 condition using HQL/SQL query:
Person ID which satisfies both these conditions "(Music < 100) and (Dance != Normal)" (in Table B) and whose Place and Country is A and AAA (in Table C).
Tables below:
[
[
[
How can I fetch these records based on this three conditions. Could someone help me.
The output should be
Record having ID as 100 in Table A since it has place and value as 'A' and 'AA'. And it also has both Music and Dance skills with Music value greater than 100 and Dance value is not like 'Normal'
select
*
from a
inner join b as music on a.id = music.person_id and music.skills = 'Music'
inner join b as dance on a.id = dance.person_id and dance.skills = 'Dance'
inner join c on a.id = c.id
where c.place = 'A' and c.country = 'AAA'
and music.score < '100'
and dance.score <> 'Normal'
You will have a problem attempting to use less than on the column "score" which is text and not numeric, see test cases below
CREATE TABLE mytable(
Value VARCHAR(6) NOT NULL PRIMARY KEY
);
INSERT INTO mytable(Value) VALUES ('100');
INSERT INTO mytable(Value) VALUES ('a');
INSERT INTO mytable(Value) VALUES ('aa');
INSERT INTO mytable(Value) VALUES ('bbb');
INSERT INTO mytable(Value) VALUES ('cccc');
INSERT INTO mytable(Value) VALUES ('99');
INSERT INTO mytable(Value) VALUES ('9');
INSERT INTO mytable(Value) VALUES ('1');
INSERT INTO mytable(Value) VALUES ('19');
select
*
from mytable where value < '100'
| value |
| :---- |
| 1 |
select
*
from mytable where value > '100'
| value |
| :---- |
| a |
| aa |
| bbb |
| cccc |
| 99 |
| 9 |
| 19 |
select
*
from mytable where cast(value as integer) > 100
ERROR: invalid input syntax for integer: "a"
db<>fiddle here
Here my row with my first select:
SELECT
user.id, analytic_youtube_demographic.age,
analytic_youtube_demographic.percent
FROM
`user`
INNER JOIN
analytic ON analytic.user_id = user.id
INNER JOIN
analytic_youtube_demographic ON analytic_youtube_demographic.analytic_id = analytic.id
Result:
---------------------------
| id | Age | Percent |
|--------------------------
| 1 |13-17| 19,6 |
| 1 |18-24| 38.4 |
| 1 |25-34| 22.5 |
| 1 |35-44| 11.5 |
| 1 |45-54| 5.3 |
| 1 |55-64| 1.6 |
| 1 |65+ | 1.2 |
| 2 |13-17| 10 |
| 2 |18-24| 10 |
| 2 |25-34| 25 |
| 2 |35-44| 5 |
| 2 |45-54| 25 |
| 2 |55-64| 5 |
| 1 |65+ | 20 |
---------------------------
The max value by user_id:
---------------------------
| id | Age | Percent |
|--------------------------
| 1 |18-24| 38.4 |
| 2 |45-54| 25 |
| 2 |25-34| 25 |
---------------------------
And I need to filter Age in ['25-34', '65+']
I must have at the end :
-----------
| id |
|----------
| 2 |
-----------
Thanks a lot for your help.
Have tried to use MAX(analytic_youtube_demographic.percent). But I don't know how to filter with the age too.
Thanks a lot for your help.
You can use the rank() function to identify the largest percentage values within each user's data set, and then a simple WHERE clause to get those entries that are both of the highest rank and belong to one of the specific demographics you're interested in. Since you can't use windowed functions like rank() in a WHERE clause, this is a two-step process with a subquery or a CTE. Something like this ought to do it:
-- Sample data from the question:
create table [user] (id bigint);
insert [user] values
(1), (2);
create table analytic (id bigint, [user_id] bigint);
insert analytic values
(1, 1), (2, 2);
create table analytic_youtube_demographic (analytic_id bigint, age varchar(32), [percent] decimal(5, 2));
insert analytic_youtube_demographic values
(1, '13-17', 19.6),
(1, '18-24', 38.4),
(1, '25-34', 22.5),
(1, '35-44', 11.5),
(1, '45-54', 5.3),
(1, '55-64', 1.6),
(1, '65+', 1.2),
(2, '13-17', 10),
(2, '18-24', 10),
(2, '25-34', 25),
(2, '35-44', 5),
(2, '45-54', 25),
(2, '55-64', 5),
(2, '65+', 20);
-- First, within the set of records for each user.id, use the rank() function to
-- identify the demographics with the highest percentage.
with RankedDataCTE as
(
select
[user].id,
youtube.age,
youtube.[percent],
[rank] = rank() over (partition by [user].id order by youtube.[percent] desc)
from
[user]
inner join analytic on analytic.[user_id] = [user].id
inner join analytic_youtube_demographic youtube on youtube.analytic_id = analytic.id
)
-- Now select only those records that are (a) of the highest rank within their
-- user.id and (b) either the '25-34' or the '65+' age group.
select
id,
age,
[percent]
from
RankedDataCTE
where
[rank] = 1 and
age in ('25-34', '65+');
I have a table like this -
create table tbl1
(
id number,
role number
);
insert into tbl1 values (1, 1);
insert into tbl1 values (2, 3);
insert into tbl1 values (1, 3);
create table tbl2
(
role number,
meaning varchar(50)
);
insert into tbl2 values (1, 'changing data');
insert into tbl2 values (2, 'move file');
insert into tbl2 values (3, 'dance');
I want the sql result like the following -
id role_meaning is_permitted
1 changing data yes
1 move file no
1 dance yes
2 changing data no
2 move file no
2 dance yes
Please help how can I do this? I have tried several methods but not sure how to do this.
You can use partitioned outer join here.
SQL Fiddle
Query 1:
select tbl1.id,
tbl2.meaning,
case when tbl1.role is NULL then 'no' else 'yes' end is_permitted
from tbl1
partition by (id) right outer join tbl2
on tbl1.role = tbl2.role
order by tbl1.id, tbl2.role
Results:
| ID | MEANING | IS_PERMITTED |
|----|---------------|--------------|
| 1 | changing data | yes |
| 1 | move file | no |
| 1 | dance | yes |
| 2 | changing data | no |
| 2 | move file | no |
| 2 | dance | yes |
I'm a new user of PostgreSQL, trying to use DISTINCT ON but I can't reach my goal.
Here's a brief sketch of my database :
files with versioning
fields with model (for form generation purpose)
n:n relations between files' versions and fields
I would like to retrieve a whole set of fields for a specified file's version.
My problem is that we could have (and we'll) empty values, ie. missing FileVersion_Field relations. I'll try to give you an example bellow :
FileVersion Field
+----------------+---------+---------+ +----------+-------+---------------+
| id_fileversion | id_file | version | | id_field | value | id_fieldmodel |
+----------------+---------+---------+ +----------+-------+---------------+
| 1 | 1 | 1 | | 1 | Smith | 1 |
| 2 | 1 | 2 | | 2 | 20 | 2 |
+----------------+---------+---------+ | 3 | 25 | 2 |
+----------+-------+---------------+
FileVersion_Field FieldModel
+----------------+----------+ +---------------+------+
| id_fileversion | id_field | | id_fieldmodel | type |
+----------------+----------+ +---------------+------+
| 1 | 1 | | 1 | Name |
| 1 | 2 | | 2 | Age |
| 2 | 3 | +---------------+------+
+----------------+----------+
In this example, I would like to get these results:
-- id_file=1 & version=1
Name | Smith
Age | 20
-- id_file=1 & version=2
Name |
Age | 25
Here's what I've tried, which doesn't work :
SELECT DISTINCT ON(FieldModel.id_fieldmodel) *
FROM File
LEFT JOIN FileVersion ON File.id_file = FileVersion.id_file
LEFT JOIN FileVersion_Field ON FileVersion.id_fileversion = FileVersion_Field.id_fileversion
LEFT JOIN Field ON FileVersion_Field.id_field = Field.id_field
RIGHT JOIN FieldModel ON (Field.id_fieldmodel = FieldModel.id_fieldmodel OR FieldModel.id_fieldmodel IS NULL)
WHERE (FieldModel.id_fieldmodel IS NOT NULL AND FileVersion.version = 2 AND File.id_file = 1)
OR (Field.id_fieldmodel IS NULL)
ORDER BY FieldModel.id_fieldmodel;
-- Sample Structure
CREATE TABLE File (
id_file integer PRIMARY KEY);
CREATE TABLE FieldModel (
id_fieldmodel integer PRIMARY KEY, type varchar(50));
CREATE TABLE FileVersion (
id_fileversion integer PRIMARY KEY,
id_file integer, version integer,
CONSTRAINT fk_fileversion_file FOREIGN KEY(id_file) REFERENCES File(id_file));
CREATE TABLE Field (
id_field integer PRIMARY KEY,
id_fieldmodel integer,
value varchar(255),
CONSTRAINT fk_field_fieldmodel FOREIGN KEY(id_fieldmodel) REFERENCES FieldModel(id_fieldmodel));
CREATE TABLE FileVersion_Field (
id_fileversion integer,
id_field integer,
PRIMARY KEY(id_fileversion, id_field),
CONSTRAINT fk_fileversionfield_fileversion FOREIGN KEY(id_fileversion) REFERENCES FileVersion(id_fileversion),
CONSTRAINT fk_fileversionfield_field FOREIGN KEY(id_field) REFERENCES Field(id_field));
-- Sample Data
INSERT INTO File (id_file) VALUES (1);
INSERT INTO FileVersion (id_fileversion, id_file, version) VALUES (1, 1, 1), (2, 1, 2);
INSERT INTO FieldModel (id_fieldmodel, type) VALUES (1, 'Name'), (2, 'Age');
INSERT INTO Field (id_field, id_fieldmodel, value) VALUES (1, 1, 'Smith'), (2, 2, '20'), (3, 2, '25');
INSERT INTO FileVersion_Field (id_fileversion, id_field) VALUES (1, 1), (1, 2), (2, 3);
7 years later, time to exorcize my daemons!
I just needed to change my way of thinking.
First, we need the list of all used FieldModel for a File, whatever the version:
SELECT DISTINCT(fm.id_fieldmodel), fm.type
FROM FieldModel fm
LEFT JOIN Field f ON fm.id_fieldmodel = f.id_fieldmodel
LEFT JOIN FileVersion_Field fvf ON f.id_field = fvf.id_field
LEFT JOIN FileVersion fv ON fv.id_fileversion = fvf.id_fileversion
WHERE fv.id_file = 1;
-- id_fieldmodel | type
-- ---------------+------
-- 1 | Name
-- 2 | Age
Now, we need the list of Field for the same File, but this time with a specified version:
SELECT f.id_fieldmodel, f.value
FROM FileVersion_Field fvv
JOIN FileVersion fv ON fv.id_fileversion = fvv.id_fileversion
JOIN Field f ON f.id_field = fvv.id_field
WHERE fv.id_file = 1 AND fv.version = 2;
-- id_fieldmodel | value
-- ---------------+-------
-- 2 | 25
All that remains is to use a LEFT JOIN on both computed tables, by allowing NULL values in the fields:
SELECT fm.type, f.value
FROM (
SELECT DISTINCT(fm.id_fieldmodel), fm.type
FROM FieldModel fm
LEFT JOIN Field f ON fm.id_fieldmodel = f.id_fieldmodel
LEFT JOIN FileVersion_Field fvf ON f.id_field = fvf.id_field
LEFT JOIN FileVersion fv ON fv.id_fileversion = fvf.id_fileversion
WHERE fv.id_file = 1
) fm
LEFT JOIN (
SELECT f.id_fieldmodel, f.value
FROM FileVersion_Field fvv
JOIN FileVersion fv ON fv.id_fileversion = fvv.id_fileversion
JOIN Field f ON f.id_field = fvv.id_field
WHERE fv.id_file = 1 AND fv.version = 2
) f ON (f.id_fieldmodel = fm.id_fieldmodel OR f.id_fieldmodel IS NULL);
-- type | value
-- ------+-------
-- Name |
-- Age | 25
Conditional joining statement : -
High level Description :-
Join table based on two columns if combination is not present then join on one table -
Detailed Table -
create table tab1
(tab1_col1 number not null,
tab1_col2 number null,
tab1_col3 varchar(10));
Lookup Table
create table lkp1
(lkp_col1 number not null,
lkp_col2 number not null,
lkp_col3 number not null,
lkp_col4 varchar(10));
Insert Statement -
tab1
insert into tab1 values (10,101,'A');
insert into tab1 values (12,101,'B');
insert into tab1 values (11,102,'C');
insert into tab1 values (13,103,'B');
insert into tab1 values (14,104,'C');
insert into tab1 values (15,108,'A');
insert into tab1 values (16,102,'D');
Lookup Table
lkp1
insert into lkp1 values (10,101,50,'PICK');
insert into lkp1 values (10,101,50,'PICK');
insert into lkp1 values (11,102,100,'SKIP');
insert into lkp1 values (11,110,50,'PICK');
insert into lkp1 values (13,103,40,'PICK');
insert into lkp1 values (13,103,60,'PICK');
insert into lkp1 values (14,199,100,'PICK');
insert into lkp1 values (15,115,80,'PICK');
insert into lkp1 values (15,115,20,'PICK');
Requirement was -
Join table based on tab1_col1=lkp_col1
and
tab1_col2=lkp_col2
Filter out lookup table data lkp_col4=SKIP
If record not present in lookup table for then give default value(99.99).
(All records from tab1 table should be selected).
I built this query and it was working fine.
SELECT tab1_col1, tab1_col2, NVL (lkp_col3, '99.99') "LKP_COL3"
FROM tab1,
(SELECT *
FROM lkp1
WHERE lkp_col4 = 'PICK') lkp
WHERE tab1_col1 = lkp_col1(+) AND tab1_col2 = lkp_col2(+)
Now requirement changed
First check if
tab1_col1=lkp_col1
and
tab1_col2=lkp_col2
If lookup table is not having data for this combination
then check again with
tab1_col1=lkp_col1
If this is not also available then give dafault value.
Database - Oracle 10g
What I have tried so far
After lot of trail and error I m able to get the output. However, is there a better or simple way to use this ?
SELECT tab1_col1, tab1_col2, LKP_COL3
FROM tab1,
(SELECT *
FROM lkp1
WHERE lkp_col4 = 'PICK') lkp
WHERE tab1_col1 = lkp_col1 AND tab1_col2 = lkp_col2
union all
SELECT tab1_col1, tab1_col2, LKP_COL3
FROM tab1,
(SELECT *
FROM lkp1
WHERE lkp_col4 = 'PICK') lkp
WHERE tab1_col1 = lkp_col1(+)
AND
LKP_COL1|| '-' || LKP_COL2 not in( SELECT tab1_col1|| '-' || tab1_col2
FROM tab1, lkp1 lkp
WHERE tab1_col1 = lkp_col1 AND tab1_col2 = lkp_col2)
order by 1
The following result:
| TAB1_COL1 | TAB1_COL2 | LKP_COL3 |
|-----------|-----------|----------|
| 10 | 101 | 50 |
| 11 | 102 | 50 |
| 12 | 101 | 99.99 |
| 13 | 103 | 40 |
| 13 | 103 | 60 |
| 14 | 104 | 100 |
| 15 | 108 | 20 |
| 15 | 108 | 80 |
| 16 | 102 | 99.99 |
was produced by this query:
SELECT DISTINCT
tab1.tab1_col1
, tab1.tab1_col2
, COALESCE(lkp1.lkp_col3, lkp2.lkp_col3, 99.99) "LKP_COL3"
FROM tab1
LEFT JOIN lkp1
ON tab1.tab1_col1 = lkp1.lkp_col1
AND tab1.tab1_col2 = lkp1.lkp_col2
AND lkp1.lkp_col4 = 'PICK'
LEFT JOIN lkp1 lkp2
ON tab1.tab1_col1 = lkp2.lkp_col1
AND lkp2.lkp_col4 = 'PICK'
ORDER BY
tab1.tab1_col1
, tab1.tab1_col2
;
DISTINCT was added because the second left (outer) join produces unwanted repetition in the output.
refer to this sqlfiddle