I have two tables.
First table is a dictionary:
test_1=> select * from first;
id | name
----+-------
1 | name1
2 | name2
3 | name3
Second table which is referenced to first table:
test_1=> select * from second;
id | number | first_id
----+--------+----------
1 | 1 | 2
2 | 1 | 3
3 | 1 | 2
4 | 2 | 3
5 | 2 | 2
6 | 3 | 3
Now I use pivot to show results:
SELECT *
FROM crosstab('SELECT
number, name, sum(number)::integer
FROM first
LEFT JOIN second ON second.first_id = first.id
GROUP BY 2, 1
ORDER BY 1, 2', $$SELECT unnest('{name1,name2,name3}'::text[])$$) AS ta (
number INTEGER,
name1 INTEGER,
name2 INTEGER,
name3 INTEGER
);
And result:
number | name1 | name2 | name3
--------+-------+-------+-------
1 | | 2 | 1
2 | | 2 | 2
3 | | | 3
And it's ok.
But now I must manually define columns - name1, name2, name3..., but columns are form dictionary table which can be changed.
So my question is - there is any possibility to get columns to pivot dynamically?
Related
I have a table with cols ID, and account type(int)
and I want to update account type to 3 for all rows, except one where given an ID there is more than one row with the same account type. so if I have
ID --- account_type
1 --- 2
1 --- 2
2 --- 1
2 --- 2
2 --- 3
I want to change one of the rows with id = 1 to have account type = 3 but leave the other one at 2 so I would like to return for the above example.
ID --- account_type
1 --- 2
1 --- 3
2 --- 1
2 --- 2
2 --- 3
I tried
UPDATE myTable
SET account_type = 3
WHERE 1 < (
SELECT COUNT(*)
FROM myTable
GROUP BY ID, account_type
HAVING COUNT(*) > 1);
but this updated every row in my db instead of just the one row with the duplicate account type so I know I did something wrong there. and this statement would set both rows with id=1 to have account_type =3 instead of just one row. How would I accomplish this?
EDIT:
I think I can fix the problem of only updating one row with:
UPDATE myTable p1
SET account_type = 3
WHERE 1 < (
SELECT COUNT(*)
FROM myTable p2
WHERE p1.primaryKey > p2.primaryKey
GROUP BY ID, account_type
HAVING COUNT(*) > 1);
but I'm still not sure why this updating every row in the db instead of the one with the duplicate account_type
So, we have table mytable:
| PrimaryKey | ID | account_type |
| ----------- | --- | ------------- |
| 1 | 1 | 2 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
| 5 | 2 | 3 |
and I think, this is Your update:
update mytable
set account_type = 3
where primarykey in (
select min(primarykey)
from mytable
group by id
)
and id not in (
select id
from mytable
where account_type = 3
);
this is the result:
| PrimaryKey | ID | account_type |
| ----------- | --- | ------------- |
| 1 | 1 | 3 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 2 | 2 |
| 5 | 2 | 3 |
I am comparing two tables (dbo.new and dbo.old) and if the first three column match and the forth column doesnst match, it has to select it. Now this shows a lot of values, and I only want to display the unique values of column2. This is the code I have now:
SELECT dbo.new.[column1], dbo.new.[column2], dbo.new.[column3], dbo.new.[column4]
FROM dbo.new
JOIN dbo.old ON dbo.new.[column1]=dbo.old.[column1]
AND dbo.new.[column2]=dbo.old.[column2]
AND dbo.new.[column3]=dbo.old.[column3]
WHERE [dbo].[new].[column4] <> [dbo].[old].[column4]
First two tables I start with:
-----------------
| 1 | 1 | 1 | 1 |
-----------------
| 2 | 1 | 2 | 2 |
-----------------
| 3 | 3 | 3 | 3 |
-----------------
| 4 | 1 | 4 | 4 |
-----------------
-----------------
| 1 | 1 | 1 | 9 |
-----------------
| 2 | 1 | 2 | 9 |
-----------------
| 3 | 3 | 3 | 9 |
-----------------
| 4 | 1 | 4 | 9 |
-----------------
This is the outcome of the query above:
-----------------
| 1 | 1 | 1 | 1 |
-----------------
| 2 | 1 | 2 | 2 |
-----------------
| 3 | 3 | 3 | 3 |
-----------------
| 4 | 1 | 4 | 4 |
-----------------
^ delete those duplicates
This is what I want to be the outcome:
-----------------
| 1 | 1 | 1 | 1 |
-----------------
| 3 | 3 | 3 | 3 |
-----------------
I tried many things like UNIQUE and DISTINCT but I cant find the solution. It doenst even need to show the first value, as long as it show onea row with the unique number. So this is correct too:
-----------------
| 4 | 1 | 4 | 4 |
-----------------
| 3 | 3 | 3 | 3 |
-----------------
Choose the ordering you need in over() to get proper rows.
SELECT TOP(1) WITH TIES
dbo.new.[column1], dbo.new.[column2], dbo.new.[column3], dbo.new.[column4]
FROM dbo.new
JOIN dbo.old ON dbo.new.[column1]=dbo.old.[column1]
AND dbo.new.[column2]=dbo.old.[column2]
AND dbo.new.[column3]=dbo.old.[column3]
where [dbo].[new].[column4] <> [dbo].[old].[column4]
ORDER BY row_number() over(partition by dbo.new.[column2] order by dbo.new.[column1])
Quick demo, runs OK sql server 2014
create table dbo.new(
column1 int,
column2 int,
column3 int,
column4 int);
create table dbo.old(
column1 int,
column2 int,
column3 int,
column4 int);
insert dbo.new values
( 1 , 1 , 1 , 1 ),
( 2 , 1 , 2 , 2 ),
( 3 , 3 , 3 , 3 ),
( 4 , 1 , 4 , 4 );
insert dbo.old values
( 1 , 1 , 1 , 9 ),
( 2 , 1 , 2 , 9 ),
( 3 , 3 , 3 , 9 ),
( 4 , 1 , 4 , 9 );
SELECT TOP(1) WITH TIES
dbo.new.[column1], dbo.new.[column2], dbo.new.[column3], dbo.new.[column4]
FROM dbo.new
JOIN dbo.old ON dbo.new.[column1]=dbo.old.[column1]
AND dbo.new.[column2]=dbo.old.[column2]
AND dbo.new.[column3]=dbo.old.[column3]
where [dbo].[new].[column4] <> [dbo].[old].[column4]
ORDER BY row_number() over(partition by dbo.new.[column2] order by dbo.new.[column1]);
Result is
column1 column2 column3 column4
1 1 1 1
3 3 3 3
It looks like you're only interested in column 2 so let's start with only selecting that.
Then add a simple: GROUP BY at the end and you're done.
SELECT N.[column2] as myvalue
FROM dbo.new N
JOIN dbo.old O
ON N.[column1]=O.[column1]
AND N.[column2]=O.[column2]
AND N.[column3]=O.[column3]
WHERE N.[column4] <> O.[column4]
GROUP BY N.[column2]
Given a cross-reference table t relating table a with b:
| id | a_id | b_id |
--------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 7 |
| 5 | 2 | 3 |
| 6 | 3 | 2 |
| 7 | 3 | 3 |
What would be the conventional way of selecting all a_id whose b_id is a superset of a given set?
For example, for the set (2,3), I would expect the result:
| a_id |
--------
| 1 |
| 3 |
Since a_id 1 and 3 are the only set of b_id that is a superset of (2,3).
The best solution I've found so far (thanks to this answer):
select id
from a
where 2 = (select count(*)
from t
where t.a_id = a.id and t.b_id in (2,3)
);
But I'd prefer to avoid calculating stuff like cardinality before running the query.
You can simply adapt the query as:
select id
from a cross join
(select count(*) as cnt
from t
where . . .
) x
where x.cnt = (select count(*)
from t
where t.a_id = a.id and t.b_id in (2,3)
);
I need get all lines from table, that have unique value in certain fields and all lines, than have null in this fields. Example:
id | name | group
-----------------
1 | One | 1
2 | Two | null
3 | Three| 3
4 | Four | 2
5 | Five | 1
6 | Six | 2
7 | Seven| null
Result:
id | name | group
-----------------
1 | One | 1
2 | Two | null
3 | Three| 3
4 | Four | 2
7 | Seven| null
How to make it in one request?
select t.id, t.name, t.`group`
from tablename t
join (select `group`, min(id) as mid
from tablename
where `group` is not null
group by `group`) x on x.mid = t.id and x.`group` = t.`group`
union all
select id, name, `group`
from tablename
where `group` is null
What would be the best way of going about doing the following insert. I've looked around, and I'm kind of stuck.
The table I Currently have (insert)
| id | order_id | item_id | type | group |
| 1 | 1 | 1 | 2 | 1 | <- type 2 represents a "header" item, or a "kit"
| 2 | 1 | 2 | 1 | 1 | <- type 1 represents a member of the "kit"
| 3 | 1 | 3 | 1 | 1 |
| 4 | 1 | 4 | 2 | 2 | <- New group means new kit
| 5 | 1 | 2 | 1 | 2 |
| 6 | 1 | 5 | 1 | 2 |
I need to insert these items into the following two tables:
1) item_entry
| id | mode | tmplt_id | item_id | parent_item_entry_id |
| 1 | 1 | 1 | NULL | NULL | <- This is a header line, mode 1
| 2 | 2 | NULL | 2 | 1 | <- This is a sub line, mode 2
| 3 | 2 | NULL | 3 | 1 | <- parent_item_entry_id references the header it belongs to
| 4 | 1 | 4 | NULL | NULL |
| 5 | 2 | NULL | 2 | 4 |
| 6 | 2 | NULL | 5 | 4 |
2) item_entry_details
| id | item_entry_id | order_id | group |
| 1 | 1 | 1 | 1 |
| 2 | 4 | 1 | 2 | <- only header information is necessary
Of course, item_entry.id is driven off of a sequence (item_entry_id_seq). Is there an elegent way of getting this to work? I currently loop through each group, first assigning a nextval() to a variable, and then I loop through each item in the group, writing to the table.
FOR recGroup IN SELECT DISTINCT group FROM insert LOOP
intParentItemEntryID := nextval('item_entry_id_seq');
FOR recLine IN SELECT * FROM insert LOOP
INSERT INTO item_entry VALUES (CASE intParentItemEntryID/DEFAULT, CASE 1/2, CASE recLine.item_id/NULL, CASE NULL/recLine.item_id, CASE NULL/intParentItemEntryID)
INSERT INTO item_entry_details VALUES (DEFAULT, intParentItemEntryID, recLine.order_id, recLine.group);
END LOOP;
END LOOP;
Is there a better way, or is the above the only way this type of insert can be done?
No need for procedural code "row-at-a-time" here, just plain old sql will suffice.
-- create the tables that the OP did not provide
DROP TABLE the_input;
CREATE TABLE the_input
( id INTEGER NOT NULL PRIMARY KEY
, order_id INTEGER NOT NULL
, item_id INTEGER NOT NULL
, ztype INTEGER NOT NULL
, zgroup INTEGER NOT NULL
);
DROP TABLE target1 ;
CREATE TABLE target1
( id INTEGER NOT NULL
, zmode INTEGER NOT NULL
, tmplt_id INTEGER
, item_id INTEGER
, parent_item_entry_id INTEGER
);
DROP TABLE target2 ;
CREATE TABLE target2
( id SERIAL NOT NULL
, item_entry_id INTEGER NOT NULL
, order_id INTEGER NOT NULL
, zgroup INTEGER NOT NULL
);
-- fil it up ...
INSERT INTO the_input
( id, order_id, item_id, ztype, zgroup ) VALUES
( 1 , 1 , 1 , 2 , 1 ) -- <- type 2 represents a "header" item, or a "kit"
,( 2 , 1 , 2 , 1 , 1 ) -- <- type 1 represents a member of the "kit"
,( 3 , 1 , 3 , 1 , 1 ) --
,( 4 , 1 , 4 , 2 , 2 ) -- <- New group means new kit
,( 5 , 1 , 2 , 1 , 2 ) --
,( 6 , 1 , 5 , 1 , 2 ) --
;
-- Do the inserts.
INSERT INTO target1(id,zmode,tmplt_id,item_id,parent_item_entry_id)
SELECT i1.id, 1, i1.id, NULL, NULL
FROM the_input i1
WHERE i1.ztype=2
UNION ALL
SELECT i2.id, 2, NULL, i2.id, ip.item_id
FROM the_input i2
JOIN the_input ip ON ip.zgroup = i2.zgroup AND ip.ztype=2
WHERE i2.ztype=1
;
INSERT INTO target2(item_entry_id,order_id,zgroup)
SELECT DISTINCT MIN(item_id),order_id, zgroup
FROM the_input i1
WHERE i1.ztype=2
GROUP BY order_id,zgroup
;
SELECT * FROM target1
ORDER BY id;
SELECT * FROM target2
ORDER BY id;
Result:
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "the_input_pkey" for table "the_input"
CREATE TABLE
INSERT 0 6
DROP TABLE
CREATE TABLE
INSERT 0 6
id | zmode | tmplt_id | item_id | parent_item_entry_id
----+-------+----------+---------+----------------------
1 | 1 | 1 | |
2 | 2 | | 2 | 1
3 | 2 | | 3 | 1
4 | 1 | 4 | |
5 | 2 | | 5 | 4
6 | 2 | | 6 | 4
(6 rows)
DROP TABLE
NOTICE: CREATE TABLE will create implicit sequence "target2_id_seq" for serial column "target2.id"
CREATE TABLE
INSERT 0 2
id | item_entry_id | order_id | zgroup
----+---------------+----------+--------
1 | 4 | 1 | 2
2 | 1 | 1 | 1
(2 rows)