Query Maximum VARRAY value - sql

How can I query for the maximum value inside a varray?
create type myWave as varray(10) of int;
create table foo (id number, yVals myWave);
insert into foo values (1, myWave(1, 8, 5));
insert into foo values (2, myWave(1, 3, 4));
insert into foo values (3, myWave(9, 5, 9));
insert into foo values (4, myWave(8, 2));
Incorrect SQL: SELECT id, MAX(yVals) maxY FROM foo
Desired output:
| id | maxY |
|----|------|
| 1 | 8 |
| 2 | 4 |
| 3 | 9 |
| 4 | 8 |

SELECT t1.ID, MAX(t2.column_value) FROM foo t1, TABLE(t1.yVals) t2 group by t1.ID

Related

PostgreSQL - Group By Two Columns And Use One As Column For Result

I have two tables: Subject and Journal as following:
Subject
id | name
----------
1 | fruit
2 | drink
3 | vege
4 | fish
and
Journal
id | subj | reference | value
------------------------------
1 | 1 | foo | 30
2 | 2 | bar | 20
3 | 1 | bar | 35
4 | 1 | bar | 10
5 | 2 | baz | 25
6 | 4 | foo | 30
7 | 4 | bar | 40
8 | 1 | baz | 20
9 | 2 | bar | 5
I want to sum Journal.value group by both subj and reference.
I know the group by clause is for this purpose, but I would expect an output as following:
reference | subj_1 | subj_2 | subj_3 | subj_4
| fruit | drink | vege | fish (even better)
---------------------------------------------
foo | 30 | | | 30
bar | 45 | 25 | | 40
baz | 20 | 25 | |
Is this possible?
You can generate a Sql Statement based on the current data.
Then use that generated Sql statement
Sample data:
create table Subject (
id serial primary key,
name varchar(30) not null
);
insert into Subject (id, name) values
(1 ,'fruit')
,(2 ,'drink')
,(3 ,'vege')
,(4 ,'fish');
create table Journal (
id int,
subj int,
reference varchar(30),
value int
);
insert into Journal
(id, subj, reference, value) values
(1, 1, 'foo', 30)
,(2, 2, 'bar', 20)
,(3, 1, 'bar', 35)
,(4, 1, 'bar' ,10)
,(5, 2, 'baz', 25)
,(6, 4, 'foo', 30)
,(7, 4, 'bar', 40)
,(8, 1, 'baz', 20)
,(9, 2, 'bar', 5);
Generate statement:
SELECT $f$SELECT * FROM crosstab(
$$SELECT DISTINCT ON (1, 2)
j.reference, 'subj_'||j.subj||'_'||s.name AS data_type, SUM(j.value) AS val
FROM Journal j
JOIN Subject s ON s.id = j.subj
GROUP BY j.reference, j.subj, s.name
ORDER BY j.reference$$
,$$VALUES ($f$ || string_agg(quote_literal(data_type), '), (') || $f$)$$)
AS x (reference text, $f$ || string_agg(quote_ident(data_type), ' int, ') || ' int)'
AS Stmt
FROM (SELECT concat('subj_', id, '_', name) AS data_type FROM Subject) x
| stmt |
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SELECT * FROM crosstab(<br> $$SELECT DISTINCT ON (1, 2)<br> j.reference, 'subj_'||j.subj||'_'||s.name AS data_type, SUM(j.value) AS val<br> FROM Journal j<br> JOIN Subject s ON s.id = j.subj<br> GROUP BY j.reference, j.subj, s.name<br> ORDER BY j.reference$$<br><br> ,$$VALUES ('subj_1_fruit'), ('subj_2_drink'), ('subj_3_vege'), ('subj_4_fish')$$)<br>AS x (reference text, subj_1_fruit int, subj_2_drink int, subj_3_vege int, subj_4_fish int) |
Run it
SELECT * FROM crosstab(
$$SELECT DISTINCT ON (1, 2)
j.reference, 'subj_'||j.subj||'_'||s.name AS data_type, SUM(j.value) AS val
FROM Journal j
JOIN Subject s ON s.id = j.subj
GROUP BY j.reference, j.subj, s.name
ORDER BY j.reference$$
,$$VALUES ('subj_1_fruit'), ('subj_2_drink'), ('subj_3_vege'), ('subj_4_fish')$$)
AS x (reference text, subj_1_fruit int, subj_2_drink int, subj_3_vege int, subj_4_fish int)
reference | subj_1_fruit | subj_2_drink | subj_3_vege | subj_4_fish
:-------- | -----------: | -----------: | ----------: | ----------:
bar | 45 | 25 | null | 40
baz | 20 | 25 | null | null
foo | 30 | null | null | 30
db<>fiddle here
This produces your desired result:
SELECT *
FROM crosstab(
'SELECT reference, subj, sum(value)
FROM journal
GROUP BY 1, 2
ORDER BY 1, 2'
, $$VALUES (1), (2), (3), (4)$$
) AS ct (reference text, fruit int, drink int, vege int, fish int);
db<>fiddle here
Except for the sort order, which seems arbitrary?
Detailed explanation and instructions:
PostgreSQL Crosstab Query

Classify records based on matching table

I have two tables: ITEMS and MATCHING_ITEMS, as below:
ITEMS:
|---------------------|------------------|
| ID | Name |
|---------------------|------------------|
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | D |
| 5 | E |
| 6 | F |
| 7 | G |
|---------------------|------------------|
MATCHING_ITEMS:
|---------------------|------------------|
| ID_1 | ID_2 |
|---------------------|------------------|
| 1 | 2 |
| 1 | 3 |
| 2 | 3 |
| 4 | 5 |
| 4 | 6 |
| 5 | 6 |
|---------------------|------------------|
The MATCHING_ITEMS table defines items that match each other, and thus belong to the same group, i.e. items 1,2, and 3 match with each other and thus belong in a group, and the same for items 4,5, and 6. Item 7 does not have a match belong to any group.
I now need to add a 'Group' column on the ITEMS table which contains a unique integer for each group, so it would look as follows:
ITEMS:
|---------------------|------------------|------------------|
| ID | Name | Group |
|---------------------|------------------|------------------|
| 1 | A | 1 |
| 2 | B | 1 |
| 3 | C | 1 |
| 4 | D | 2 |
| 5 | E | 2 |
| 6 | F | 2 |
| 7 | G | NULL |
|---------------------|------------------|------------------|
So far I have been using a stored procedure to do this, looping over each line in the MATCHING_ITEMS table and updating the ITEMS table with a group value. The problem is that I eventually need to do this for a table containing millions of records, and the looping method is far too slow.
Is there a way that I can achieve this without using a loop?
If you have all pairs of matches in the matching table, then you can just use the minimum id to assign the group. For this:
select i.*,
(case when grp_id is not null
then dense_rank() over (order by grp_id)
end) as grouping
from items i left join
(select mi.id_1, least(mi.id1, min(mi.id2)) as grp_id
from matching_items mi
group by mi.id_1
) mi
on i.id = mi.id_1;
Note: This works only if all pairs are in the matching items table. Otherwise, you will need a recursive/hierarchical query to get all the pairs.
You could use min and max at first, then dense_rank to assign group numbers:
select id, name, dense_rank() over (order by mn, mx) grp
from (
select distinct id, name,
min(id_1) over (partition by name) mn,
max(id_2) over (partition by name) mx
from items left join matching_items on id in (id_1, id_2))
order by id
demo
The pairs 2,3 and 5,6 in the Matching_items table seem redundant as they could be derived (if I am reading your question right)
Here is how I did it. I just reused id_1 from your example as the group no:
create table
items (
ID number,
name varchar2 (2)
);
insert into items values (1, 'A');
insert into items values (2, 'B');
insert into items values (3, 'C');
insert into items values (4, 'D');
insert into items values (5, 'E');
insert into items values (6, 'F');
insert into items values (7, 'G');
create table
matching_items (
ID number,
ID_2 number
);
insert into matching_items values (1, 2);
insert into matching_items values (1, 3);
insert into matching_items values (2, 3);
insert into matching_items values (4, 5);
insert into matching_items values (4, 6);
insert into matching_items values (5, 6);
with new_grp as
(
select id, id_2, id as group_no
from matching_items
where id in (select id from items)
and id not in (select id_2 from matching_items)),
assign_grp as
(
select id, group_no
from new_grp
union
select id_2, group_no
from new_grp)
select items.id, name, group_no
from items left outer join assign_grp
on items.id = assign_grp.id;

Optimization of a sql-query with exists

I have a table:
+----+---------+-----------+--------------+-----------+
| id | item_id | attr_name | string_value | int_value |
+----+---------+-----------+--------------+-----------+
| 1 | 1 | 1 | prop_str_1 | NULL |
| 2 | 1 | 2 | prop_str_2 | NULL |
| 3 | 1 | 3 | NULL | 2 |
| 4 | 2 | 1 | prop_str_1 | NULL |
| 5 | 2 | 2 | prop_str_3 | NULL |
| 6 | 2 | 3 | NULL | 2 |
| 7 | 3 | 1 | prop_str_4 | NULL |
| 8 | 3 | 2 | prop_str_2 | NULL |
| 9 | 3 | 3 | NULL | 1 |
+----+---------+-----------+--------------+-----------+
And I want to select item_id with specific values for the attributes. But this is complicated by the fact that the fetching needs to do on several attributes. I've got to do it just using exists:
select *
from item_attribute as attr
where (name = 1 and string_value = 'prop_str_1')
and exists
(select item_id
from item_attribute
where item_id = attr.item_id and name = 2 and string_value = 'prop_str_2')
But the number of attributes can be increased, and therefore nested queries with exists will increase.
How can I rewrite this query to reduce the nested queries?
UPD:
create table item_attribute(
id int not null,
item_id int not null,
attr_name int not null,
string_value varchar(50),
int_value int,
primary key (id)
);
insert into item_attribute values (1, 1, 1, 'prop_str_1', NULL);
insert into item_attribute values (2, 1, 2, 'prop_str_2', NULL);
insert into item_attribute values (3, 1, 3, NULL, 2);
insert into item_attribute values (4, 2, 1, 'prop_str_1', NULL);
insert into item_attribute values (5, 2, 2, 'prop_str_3', NULL);
insert into item_attribute values (6, 2, 3, NULL, 2);
insert into item_attribute values (7, 3, 1, 'prop_str_4', NULL);
insert into item_attribute values (8, 3, 2, 'prop_str_2', NULL);
insert into item_attribute values (9, 3, 3, NULL, 1);
See if this works for you. It in essence does the same thing... Your first qualifier is that a given attribute name = 1 and string = 'prop_str_1', but then self-joins to attribute table again on same ID but second attribute and string
select
attr.*
from
item_attribute attr
JOIN item_attribute attr2
ON attr.item_id = attr2.item_id
and attr2.name = 2
and attr2.string_value = 'prop_str_2'
where
attr.name = 1
and string_value = 'prop_str_1'
I would also have an index on your table on (name, string_value, item_id) to increase performance of where and join conditions.

Rank of partitions in T-SQL

Given the following table:
CREATE TABLE #values (ID int, TYPE nchar(2), NUMBER int)
INSERT INTO #values values (1, 'A', 0)
INSERT INTO #values values (2, 'A', 0)
INSERT INTO #values values (3, 'B', 1)
INSERT INTO #values values (4, 'A', 1)
INSERT INTO #values values (5, 'B', 2)
SELECT * FROM #values
I would like to generate this table:
Id | T | N | COUNT
------------------
1 | A | 0 | 1000
2 | A | 0 | 1000
3 | B | 1 | 1001
4 | A | 1 | 1002
5 | B | 2 | 1003
How can I do this in T-SQL?
I've been fiddling with ROW_NUMBER() OVER(PARTITION BY) but this does not solve the problem, as it resets the count at each partition, which is not what I would like to do.
I think you're looking for dense_rank:
SELECT
ID,
TYPE,
NUMBER,
DENSE_RANK() over (order by TYPE, Number)
FROM #values
This produces
1 A 0 1
2 A 0 1
4 A 1 2
3 B 1 3
5 B 2 4

Help with MySQL statement

I have written the following SQL statement in MySQL :
USE my_database;
SELECT * FROM some_table WHERE some_column IN (1, 2, 3);
This returns a set of rows that have a column value which is a key into a row of another table (call it some_other_table).
a b c d <--this is the column with the key
1
2
3
I want to say, look up all of the rows in another table with value 1, and do something (null out some column)
Any help is appreciated.
Yes, you can use the multiple-table UPDATE syntax:
UPDATE some_other_table
JOIN some_table ON (some_table.some_key = some_other_table.id)
SET some_other_table.some_field = NULL
WHERE some_table.some_column IN (1, 2, 3);
Example:
CREATE TABLE some_table (id int, some_column int, some_key int);
CREATE TABLE some_other_table (id int, some_field int);
INSERT INTO some_table VALUES (1, 1, 1);
INSERT INTO some_table VALUES (2, 2, 2);
INSERT INTO some_table VALUES (3, 3, 3);
INSERT INTO some_table VALUES (4, 4, 4);
INSERT INTO some_table VALUES (5, 5, 5);
INSERT INTO some_other_table VALUES (1, 10);
INSERT INTO some_other_table VALUES (2, 20);
INSERT INTO some_other_table VALUES (3, 30);
INSERT INTO some_other_table VALUES (4, 40);
Before:
SELECT * FROM some_table;
+------+-------------+----------+
| id | some_column | some_key |
+------+-------------+----------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
| 4 | 4 | 4 |
| 5 | 5 | 5 |
+------+-------------+----------+
5 rows in set (0.00 sec)
SELECT * FROM some_other_table;
+------+------------+
| id | some_field |
+------+------------+
| 1 | 10 |
| 2 | 20 |
| 3 | 30 |
| 4 | 40 |
+------+------------+
4 rows in set (0.00 sec)
After:
SELECT * FROM some_table;
+------+-------------+----------+
| id | some_column | some_key |
+------+-------------+----------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
| 4 | 4 | 4 |
| 5 | 5 | 5 |
+------+-------------+----------+
5 rows in set (0.00 sec)
SELECT * FROM some_other_table;
+------+------------+
| id | some_field |
+------+------------+
| 1 | NULL |
| 2 | NULL |
| 3 | NULL |
| 4 | 40 |
+------+------------+
4 rows in set (0.00 sec)
UPDATE: Further to comments below.
Another example:
CREATE TABLE amir_effective_reference (class int, inst int, rln int, rclass int, rinst int, chg int, typ int);
CREATE TABLE amir_effective_change (chg int, txn int, rltn int, entry int, effective int);
INSERT INTO amir_effective_reference VALUES (1, 100, 1, 50, 20, 10, 5000);
INSERT INTO amir_effective_change VALUES (10, 100, 100, 500, 200);
Result:
UPDATE amir_effective_change
JOIN amir_effective_reference ON (amir_effective_reference.chg = amir_effective_change.chg)
SET amir_effective_change.effective = NULL
WHERE amir_effective_change.rltn IN (100);
SELECT * FROM amir_effective_change;
+------+------+------+-------+-----------+
| chg | txn | rltn | entry | effective |
+------+------+------+-------+-----------+
| 10 | 100 | 100 | 500 | NULL |
+------+------+------+-------+-----------+
1 row in set (0.00 sec)