I am using postgresql 8.3 and I have a simple sql query:
SELECT a.id,a.bpm_process_instance_id,a.actor_id
FROM bpm_task_instance a
WHERE a.bpm_process_instance_id IN
(
SELECT bpm_process_instance_id
FROM incident_info
WHERE status = 12
AND registrant = 23
)
so, I got a result set like this:
id instance_id actor_id
150 53 24
147 53 26
148 53 25
161 57 26
160 57 26
158 57 24
165 58 23
166 58 24
167 58 24
now, I want to get the max id by instance_id, and the result is like blew
id instance_id actor_id
150 53 24
161 57 26
167 58 23
how could I get the result ? I use the following sql, but get an error.
ERROR: relation "x" does not exist
SELECT *
FROM (SELECT a.id,a.bpm_process_instance_id,a.actor_id
FROM bpm_task_instance a
WHERE a.bpm_process_instance_id IN
(
SELECT bpm_process_instance_id
FROM incident_info
WHERE status = 12
AND registrant = 23
)
) AS x
WHERE x.id = (
SELECT max(id)
FROM x
WHERE bpm_process_instance_id = x.bpm_process_instance_id
)
anyone who can help me , thanks a lot!
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE the_table
( id INTEGER NOT NULL
, instance_id INTEGER NOT NULL
, actor_id INTEGER NOT NULL
);
INSERT INTO the_table(id, instance_id, actor_id) VALUES
(150,53,24) ,(147,53,26) ,(148,53,25)
,(161,57,26) ,(160,57,26) ,(158,57,24)
,(165,58,23) ,(166,58,24) ,(167,58,24)
;
SELECT id, instance_id, actor_id
FROM the_table dt
WHERE NOT EXISTS (
SELECT *
FROM the_table nx
WHERE nx.instance_id = dt.instance_id
AND nx.id > dt.id
);
Result (note: the last row differs!):
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 9
id | instance_id | actor_id
-----+-------------+----------
150 | 53 | 24
161 | 57 | 26
167 | 58 | 24
(3 rows)
UPDATE: this is the query including the other subquery and the missing table, and the original (ugly) column names, all packed into a CTE:
WITH zcte AS (
SELECT ti.id AS id
, ti.bpm_process_instance_id AS instance_id
, ti.actor_id AS actor_id
FROM bpm_task_instance ti
WHERE EXISTS ( SELECT * FROM incident_info ii
WHERE ii.bpm_process_instance_id = ti.bpm_process_instance_id
AND ii.status = 12
AND ii.registrant = 23
)
)
SELECT id, instance_id, actor_id
FROM zcte dt
WHERE NOT EXISTS (
SELECT *
FROM zcte nx
WHERE nx.instance_id = dt.instance_id
AND nx.id > dt.id
);
UPDATE addendum:
Oops, the bad news is that 8.3 did not have CTE's yet. (think about upgrading). The good news is: as a workaround you could make zcte () as a (temporary) VIEW, and refer to that instead.
try this:
select a.id,a.bpm_process_instance_id,a.actor_id
from bpm_task_instance A
inner join
(select max(a.id) as id,a.bpm_process_instance_id
from bpm_task_instance a
where a.bpm_process_instance_id in
( select bpm_process_instance_id
from incident_info
where status = 12 and registrant = 23
)
group by a.bpm_process_instance_id)B
on A.bpm_process_instance_id=B.bpm_process_instance_id
and A.id=B.id
#wildplasser
SELECT dt.* FROM
(
SELECT id,bpm_process_instance_id,actor_id
FROM bpm_task_instance WHERE bpm_process_instance_id in
(
SELECT bpm_process_instance_id FROM incident_info
WHERE status = 12 and registrant = 23
)
) as dt
WHERE NOT EXISTS (
SELECT *
FROM bpm_task_instance nx
WHERE nx.bpm_process_instance_id = dt.bpm_process_instance_id
AND nx.id > dt.id
)
ORDER BY id asc
At large scale, the DISTINCT ON syntax is sometimes faster than the perfectly valid answers already given.
SELECT DISTINCT ON (instance_id)
id, instance_id, actor_id
FROM the_table dt
ORDER BY instance_id, id DESC;
Once you get used to this syntax, you may find it easier to read than the alternatives. Inside the parentheses in the DISTINCT ON clause you put the list of columns which should be unique, and the ORDER BY clause must start with matching columns and continue with enough columns to ensure that the one you want to keep comes first.
Related
I don't understand why my query doesn't group results of count by the column I specified. Instead it counts all occurrences of outcome_id in the 'un' subtable.
What am I missing there?
The full structure of my sample database and the query I tried are here:
https://www.db-fiddle.com/f/4HuLpTFWaE2yBSQSzf3dX4/4
CREATE TABLE combination (
combination_id integer,
ticket_id integer,
outcomes integer[]
);
CREATE TABLE outcome (
outcome_id integer,
ticket_id integer,
val double precision
);
insert into combination
values
(510,188,'{52,70,10}'),
(511,188,'{52,56,70,18,10}'),
(512,188,'{55,70,18,10}'),
(513,188,'{54,71,18,10}'),
(514,189,'{52,54,71,18,10}'),
(515,189,'{55,71,18,10,54,56}')
;
insert into outcome
values
(52,188,1.3),
(70,188,2.1),
(18,188,2.6),
(56,188,2),
(55,188,1.1),
(54,188,2.2),
(71,188,3),
(10,188,0.5),
(54,189,2.2),
(71,189,3),
(18,189,2.6),
(55,189,2)
with un AS (
SELECT combination_id, unnest(outcomes) outcome
FROM combination c JOIN
outcome o
on o.ticket_id = c.ticket_id
GROUP BY 1,2
)
SELECT combination_id, cnt
FROM (SELECT un.combination_id,
COUNT(CASE WHEN o.val >= 1.3 THEN 1 END) as cnt
FROM un JOIN
outcome o
on o.outcome_id = un.outcome
GROUP BY 1
) x
GROUP BY 1, 2
ORDER BY 1
Expected result should be:
510 2
511 4
512 2
513 3
514 4
515 4
Assuming, you have these PK constraints:
CREATE TABLE combination (
combination_id integer PRIMARY KEY
, ticket_id integer
, outcomes integer[]
);
CREATE TABLE outcome (
outcome_id integer
, ticket_id integer
, val double precision
, PRIMARY KEY (ticket_id, outcome_id)
);
and assuming this objective:
For each row in table combination, count the number of array elements in outcomes for which there is at least one row with matching outcome_id and ticket_id in table outcome - and val >= 1.3.
Assuming above PK, this burns down to a much simpler query:
SELECT c.combination_id, count(*) AS cnt
FROM combination c
JOIN outcome o USING (ticket_id)
WHERE o.outcome_id = ANY (c.outcomes)
AND o.val >= 1.3
GROUP BY 1
ORDER BY 1;
This alternative might be faster with index support:
SELECT c.combination_id, count(*) AS cnt
FROM combination c
CROSS JOIN LATERAL unnest(c.outcomes) AS u(outcome_id)
WHERE EXISTS (
SELECT
FROM outcome o
WHERE o.outcome_id = u.outcome_id
AND o.val >= 1.3
AND o.ticket_id = c.ticket_id -- ??
)
GROUP BY 1
ORDER BY 1;
Plus, it does not require the PK on outcome. Any number of matching rows still count as 1, due to EXISTS.
db<>fiddle here
As always, the best answer depends on the exact definition of setup and requirements.
A simpler version of #forpas answer:
-- You don't need to join to outcomes in the "with" statement.
with un AS (
SELECT combination_id, ticket_id, unnest(outcomes) outcome
FROM combination c
-- no need to join to outcomes here
GROUP BY 1,2,3
)
SELECT combination_id, cnt FROM
(
SELECT un.combination_id,
COUNT(CASE WHEN o.val >= 1.3 THEN 1 END) as cnt
FROM un
JOIN outcome o on o.outcome_id = un.outcome
and o.ticket_id = un.ticket_id
GROUP BY 1
)x
GROUP BY 1,2
ORDER BY 1
As others have pointed out, the expected result for 514 should be 3 based on your input data.
I'd also like to suggest that using full field names in the group by and order by clauses makes queries easier to debug and maintain going forward.
You need to join on ticket_id also:
with un AS (
SELECT c.combination_id, c.ticket_id, unnest(c.outcomes) outcome
FROM combination c JOIN outcome o
on o.ticket_id = c.ticket_id
GROUP BY 1,2,3
)
SELECT combination_id, cnt
FROM (SELECT un.combination_id, un.ticket_id,
COUNT(CASE WHEN o.val >= 1.3 THEN 1 END) as cnt
FROM un JOIN outcome o
on o.outcome_id = un.outcome and o.ticket_id = un.ticket_id
GROUP BY 1,2
) x
GROUP BY 1, 2
ORDER BY 1
See the demo.
Results:
> combination_id | cnt
> -------------: | --:
> 510 | 2
> 511 | 4
> 512 | 2
> 513 | 3
> 514 | 3
> 515 | 4
I have two tables I have to retrieve data from both table with out duplication of rows .
there is table (a)
BILL_DATE BILL_AMOUNT
----------
20160208 94
20160208 140
20160208 30
20160208 25
20160208 14
20160208 6
20160208 35
20160208 20
20160208 35
=======================
second table is (b)
and i want to result like
You could use a full outer join on the date in order to take the relevant row from whichever table has it:
SELECT COALESCE(bill_date, reach_date) AS [date],
bill_date, bill_amount,
reach_date AS [recharge_date], reach_amount AS [recharge_amount]
FROM a
FULL OUTER JOIN b on a.bill_date = b.reach_date
Question is not clear but if you want 13 rows
select bill_date as "DATE", bill_date, bill_amount, null as "recharge_date" , null as "recharge_amount"
from a
union all
select rech_date , null , null , rech_date , rech_amount
from b;
Not clear how you are going to get 100, 200, 300, 400 for rech_amount
Try this:
SELECT ISNULL(X.BILL_DATE,X.RECHARGE_DATE) AS DATE,X.*
FROM (
SELECT BILL_DATE,BILL_AMOUNT as BILL_AMT,null as RECHARGE_DATE,null as RECHARGE_AMT
FROM Tablea
UNION ALL
SELECT null as BILL_DATE,null as BILL_AMT,RECH_DATE as RECHARGE_DATE,RECH_AMOUNT as RECHARGE_AMT
FROM Tableb
) X
Basically, I've got the following table:
ID | Amount
AA | 10
AA | 20
BB | 30
BB | 40
CC | 10
CC | 50
DD | 20
DD | 60
EE | 30
EE | 70
I need to get unique entries in each column as in following example:
ID | Amount
AA | 10
BB | 30
CC | 50
DD | 60
EE | 70
So far following snippet gives almost what I wanted, but first_value() may return some value, which isn't unique in current column:
first_value(Amount) over (partition by ID)
Distinct also isn't helpful, as it returns unique rows, not its values
EDIT:
Selection order doesn't matter
This works for me, even with the problematic combinations mentioned by Dimitri. I don't know how fast that is for larger volumes though
with ids as (
select id, row_number() over (order by id) as rn
from data
group by id
), amounts as (
select amount, row_number() over (order by amount) as rn
from data
group by amount
)
select i.id, a.amount
from ids i
join amounts a on i.rn = a.rn;
SQLFiddle currently doesn't work for me, here is my test script:
create table data (id varchar(10), amount integer);
insert into data values ('AA',10);
insert into data values ('AA',20);
insert into data values ('BB',30);
insert into data values ('BB',40);
insert into data values ('CC',10);
insert into data values ('CC',50);
insert into data values ('DD',20);
insert into data values ('DD',60);
insert into data values ('EE',30);
insert into data values ('EE',70);
Output:
id | amount
---+-------
AA | 10
BB | 20
CC | 30
DD | 40
EE | 50
I suggest using row_number() like this:
select ID ,Amount
from (
select ID ,Amount, row_number() over(partition by id order by 1) as rn
from yourtable
)
where rn = 1
However your expected results don't conform to a discrenable order, some are the first/lowest while some the last/highest so I wasn't sure what to include for the ordering.
My solution implements recursive with and makes following: first - select minival values of ID and amount, then for every next level searches values of ID and amount, which are more than already choosed (this provides uniqueness), and at the end query selects 1 row for every value of recursion level. But this is not an ultimate solution, because it is possible to find a combination of source data, where query will not work (I suppose, that such solution is impossible, at least in SQL).
with r (id, amount, lvl) as (select min(id), min(amount), 1
from t
union all
select t.id, t.amount, r.lvl + 1
from t, r
where t.id > r.id and t.amount > r.amount)
select lvl, min(id), min(amount)
from r
group by lvl
order by lvl
SQL Fiddle
I knew that there is an elegant solution! Thanks to friend of mine for a tip:
select max(ID), mAmount from (
select ID, max(Amount) mAmount from table group by ID
)
group by mAmount;
Maybe something like this can solve:
WITH tx AS
( SELECT ROWNUM ROW_NUMBER,
t.id,
t.amount
FROM test t
INNER JOIN test t2
ON t.id = t2.id
AND t.amount != t2.amount
ORDER BY t.id)
SELECT tx1.id, tx1.amount
FROM tx tx1
LEFT JOIN tx tx2
ON tx1.id = tx2.id
AND tx1.ROW_NUMBER > tx2.ROW_NUMBER
WHERE tx2.ROW_NUMBER IS NULL
i dont have any idea to get max record value from many records that have same id and joined with another table. i hope someone could help me here.
this the table:
order_id name result1 result2 result3 max_date admin seq2 extern
517 A 97 65 77 23-DEC-14 Rey 2 null
617 B null null null null 5 6
517 A null null null 18-DEC-14 Roo 1 2
617 B 44 78 80 22-DEC-14 Tow 6 5
result that i want is like this:
order_id name result1 result2 result3 max_date admin seq2 extern
517 A 97 65 77 23-DEC-14 Rey 2 2
617 B 44 78 80 22-DEC-14 Tow 6 6
i try this query, but it just gave one result:
WITH t AS
(SELECT x.order_id,x.name,x.extern_order_status as extern,y.result1, y.result2, y.result3,
t_logging_max.xd1 as max_date,
p_agent_admin.username as admin,
MAX(y.seq) AS seq2
FROM t_order_demand x
LEFT OUTER JOIN
(
SELECT id,xn1, MAX(xd1) AS xd1
FROM t_logging
GROUP BY id,xn1
) t_logging_max ON x.order_id = t_logging_max.id
LEFT JOIN
(
SELECT agent_id,username
FROM p_agent
GROUP BY agent_id,username
) p_agent_admin ON t_logging_max.xn1 = p_agent_admin.agent_id
JOIN t_order_log y
ON x.order_id = y.order_id
and y.order_id LIKE '%10%'
GROUP BY x.order_id,x.name,x.extern_order_status,y.result1, y.result2,y.result3,t_logging_max.xd1,p_agent_admin.username)
SELECT * FROM t WHERE (t.seq2) IN (SELECT MAX(tt.seq2) FROM t tt);
actually there must be more than 2 records that id has %10%
hope u could help me. thanks.
Group by with Max Aggregate should work. Except the Admin Column which is holding a varchar value.
SELECT order_id,
NAME,
Max(result1) result1,
Max(result2) result2,
Max(result3) result3,
Max(max_date) max_date,
Max(admin) admin,
Max(seq2) seq2,
Max(extern) extern
FROM tablename
GROUP BY order_id,
NAME
I'm currently dealing with two tables.
One table contains a set of columns like ID, NAME, AGE, TEAM, SCHOOL, and so forth in a table called PRIMARY_TABLE
And I also have an audit table called SECONDARY_TABLE that records updates in the aforementioned values over time.
I have ATTRIBUTE, PREV_VALUES and RECORD_ID columns in this table. It has the following attributes:
the RECORD_ID column corresponds to the ID column of PRIMARY_TABLE
the ATTRIBUTE column will store the column of the PRIMARY_TABLE that is being altered.
For example, if I have
132 NIKO 18 LANCERS JESUIT
143 KEENAN 25 RAIDERS ROCKLAND
in my first table and
132 'AGE' 22
132 'NAME' STEVAN
in my second,
then I want a combined table that has
132 NIKO 18 LANCERS JESUIT
132 NIKO 22 LANCERS JESUIT
132 STEVAN 22 LANCERS JESUIT
143 KEENAN 25 RAIDERS ROCKLAND .
The issue I have a hard time getting around is preserving the values in the unaffected rows. It seems like any idea I have for joining the two tables together won't work for this reason.
Any thoughts? I think the only solution is to create a stored procedure for this. If you need clarification, let me know as well.
EDIT
One more thing...
Here's another thing. The audit table also has a "time_of_change" column. If multiple rows have the same time of change for an ID, then instead of having multiple rows in our resulting table, there should be only one more row.
For example, if our audit table had
132 'AGE' 22 1:00
132 'NAME' STEVAN 1:00
Then instead of having
132 STEVAN 18 LANCERS JESUIT
132 NIKO 22 LANCERS JESUIT
added, there should only be one added row of
132 STEVAN 22 LANCERS JESUIT.
I can't think of any possible way to do this either.
UPDATE2 If you were to have a column with a datetime of an update in the secondary_table (lets call it updated_at) then you can order the resultset appropriately.
SELECT id, name, age, team, school, GETDATE() updated_at
FROM primary_table
UNION ALL
SELECT p.id,
CASE WHEN s.attribute = 'NAME'
THEN s.prev_values ELSE p.name END name,
CASE WHEN s.attribute = 'AGE'
THEN s.prev_values ELSE p.age END age,
CASE WHEN s.attribute = 'TEAM'
THEN s.prev_values ELSE p.team END team,
CASE WHEN s.attribute = 'SCHOOL'
THEN s.prev_values ELSE p.school END school,
updated_at
FROM primary_table p JOIN secondary_table s
ON p.ID = s.record_id
ORDER BY id, updated_at DESC
Here is SQLFiddle demo
UPDATE1 A version with one UNION and conditional output with CASE
SELECT *
FROM primary_table
UNION ALL
SELECT p.id,
CASE WHEN s.attribute = 'NAME'
THEN s.prev_values ELSE p.name END name,
CASE WHEN s.attribute = 'AGE'
THEN s.prev_values ELSE p.age END age,
CASE WHEN s.attribute = 'TEAM'
THEN s.prev_values ELSE p.team END team,
CASE WHEN s.attribute = 'SCHOOL'
THEN s.prev_values ELSE p.school END school
FROM primary_table p JOIN secondary_table s
ON p.ID = s.record_id
ORDER BY id
Here is SQLFiddle demo
Original version with UNIONs
SELECT *
FROM primary_table
UNION ALL
SELECT p.id, s.prev_values, p.age, p.team, p.school
FROM primary_table p JOIN secondary_table s
ON p.ID = s.record_id
AND s.attribute = 'NAME'
UNION ALL
SELECT p.id, p.name, s.prev_values, p.team, p.school
FROM primary_table p JOIN secondary_table s
ON p.ID = s.record_id
AND s.attribute = 'AGE'
ORDER BY id
Output:
| ID | NAME | AGE | TEAM | SCHOOL |
-------------------------------------------
| 132 | NIKO | 18 | LANCERS | JESUIT |
| 132 | STEVAN | 18 | LANCERS | JESUIT |
| 132 | NIKO | 22 | LANCERS | JESUIT |
| 143 | KEENAN | 25 | RAIDERS | ROCKLAND |
Here is SQLFiddle demo
This is not perfect but try this one:
SELECT * FROM Primary_Table
UNION ALL
SELECT * FROM
(SELECT A.ID, A.Name, B.GetAge as Age, A.School
FROM Primary_Table A INNER JOIN (SELECT Record_id, CONVERT(int,Value) as GetAge FROM Secondary_Table
WHERE Attrib='AGE' ) B
ON A.ID = B.Record_id) UAge
UNION ALL
SELECT * FROM
(SELECT A.ID, B.GetName as Name, A.Age, A.School
FROM Primary_Table A INNER JOIN (SELECT Record_id, Value as GetName FROM Secondary_Table
WHERE Attrib='NAME' ) B
ON A.ID = B.Record_id) UName
ORDER BY ID
Here's some script to get you started. You have a big job ahead of you. Things are made quite a bit more difficult if you don't have the current values in the audit table (which it seems like, so I built my script with that assumption).
WITH Curr AS (
SELECT
Record_ID = T.ID,
V.*,
Time_Of_Change = Convert(datetime, 0)
FROM
dbo.Team T
CROSS APPLY (VALUES
('Name', Convert(varchar(20), T.Name)),
('Age', Convert(varchar(20), T.Age)),
('Team', Convert(varchar(20), T.Team)),
('School', Convert(varchar(20), T.School))
) V (Attribute, Prev_Values)
),
Data AS (
SELECT
H.Record_ID,
H.Time_Of_Change,
C.Attribute,
A.Prev_Values
FROM
(
SELECT DISTINCT Record_ID, Time_Of_Change
FROM dbo.Audit
WHERE TableName = 'Team'
UNION ALL
SELECT DISTINCT ID, GetDate()
FROM dbo.Team
) H
CROSS JOIN (VALUES
('Name'), ('Age'), ('Team'), ('School')
) C (Attribute)
CROSS APPLY (
SELECT TOP 1 *
FROM (
SELECT Record_ID, Attribute, Prev_Values, Time_Of_Change
FROM dbo.Audit
WHERE TableName = 'Team'
UNION ALL
SELECT *
FROM Curr
) A
WHERE
H.Record_ID = A.Record_ID
AND H.Time_Of_Change >= A.Time_Of_Change
AND C.Attribute = A.Attribute
ORDER BY
A.Time_Of_Change DESC
) A
)
SELECT *
FROM
Data
PIVOT (Max(Prev_Values) FOR Attribute IN (Name, Age, Team, School)) P
;
See a Live Demo at SQL Fiddle
You can very easily create a view for each table you want to reconstruct the history of. Build a stored procedure that queries the INFORMATION_SCHEMA.COLUMNS view and builds something similar to what I've given you. Run the SP once after any table change, and it will update the views.
I noticed that my script is not quite right--it's showing 2 rows instead of 3 for the edits. Plus, there's no valid time for the rows for which there are no audit rows. But that part makes sense. In any case, you have a big job ahead of you...