WHERE statement with duplicate column names over JOIN - PostgreSQL - sql

I am trying to join two tables, a plans table and a plan_details table. Below are two examples of what the tables look like.
PLANS Table
+---------+------+-----------+
| user_id | plan | is_active |
+---------+------+-----------+
| 1 | 10 | true |
| 1 | 11 | false |
| 2 | 11 | true |
PLAN_DETAILS Table
+---------+------+-------+-----------+
| plan_id | cost | price | is_active |
+---------+------+-------+-----------+
| 10 | 19 | 199 | true |
| 11 | 13 | 149 | true |
I only want to only pull the active plan cost and the price related to each user. Right now, my knex statement is:
knex('plans')
.where({
user_id: 1,
is_active: 'true'
})
.select(
'plans.plan',
'plan_details.cost',
'plan_details.price'
)
.join('plan_details as plan_details', 'plan_details.plan_id', 'plans.plan')
.then(function (user_plan_id) {
console.log(user_plan_id);
});
Now if I keep the is_active: 'true' in there then I get a Unhandled rejection error: column reference "is_active" is ambiguous. If I take out the is_active part, well then I get information for both of the plans that reference the user even though I only want the info regarding which plans are active for the user.
How do I get only the active plans for a user? I am using KNEX.JS as my ORM, but I am happy to use raw SQL as well for this.

With knex this should do:
knex('plans')
.select(
'plans.plan',
'plan_details.cost',
'plan_details.price'
)
.join('plan_details as plan_details', 'plan_details.plan_id', 'plans.plan')
.where('plan.user_id', 1)
.where('plan.is_active', true)
.then(function (user_plan_id) {
console.log(user_plan_id);
});

SQL would be similar to:
select
plans.plan,
plan_details.cost,
plan_details.price
from plan
join plan_details on plans.plan = plan_details.plan_id
where plan.is_active

Related

Why is this Query not Updateable?

I was looking to provide an answer to this question in which the OP has two tables:
Table1
+--------+--------+
| testID | Status |
+--------+--------+
| 1 | |
| 2 | |
| 3 | |
+--------+--------+
Table2
+----+--------+--------+--------+
| ID | testID | stepID | status |
+----+--------+--------+--------+
| 1 | 1 | 1 | pass |
| 2 | 1 | 2 | fail |
| 3 | 1 | 3 | pass |
| 4 | 2 | 1 | pass |
| 5 | 2 | 2 | pass |
| 6 | 3 | 1 | fail |
+----+--------+--------+--------+
Here, the OP is looking to update the status field for each testID in Table1 with pass if the status of all stepID records associated with the testID in Table2 have a status of pass, else Table1 should be updated with fail for that testID.
In this example, the result should be:
+--------+--------+
| testID | Status |
+--------+--------+
| 1 | fail |
| 2 | pass |
| 3 | fail |
+--------+--------+
I wrote the following SQL code in an effort to accomplish this:
update Table1 a inner join
(
select
b.testID,
iif(min(b.status)=max(b.status) and min(b.status)='pass','pass','fail') as v
from Table2 b
group by b.testID
) c on a.testID = c.testID
set a.testStatus = c.v
However, MS Access reports the all-too-familiar, 'operation must use an updateable query' response.
I know that a query is not updateable if there is a one-to-many relationship between the record being updated and the set of values, but in this case, the aggregated subquery would yield a one-to-one relationship between the two testID fields.
Which left me asking, why is this query not updateable?
You're joining in a query with an aggregate (Max).
Aggregates are not updateable. In Access, in an update query, every part of the query has to be updateable (with the exception of simple expressions, and subqueries in WHERE part of your query), which means your query is not updateable.
You can work around this by using domain aggregates (DMin and DMax) instead of real ones, but this query will take a large performance hit if you do.
You can also work around it by rewriting your aggregates to take place in an EXISTS or NOT EXISTS clause, since that's part of the WHERE clause thus doesn't need to be updateable. That would likely minimally affect performance, but means you have to split this query in two: 1 query to set all the fields to "pass" that meet your condition, another to set them to "fail" if they don't.

Limiting subqueries in SQL

I have a situation which is a little hard to describe. I'll try to explain with an example and the result which I want.
I have three tables like so
Employee
| id | Name |
|----+-------|
| 1 | Alice |
| 2 | Bob |
| 3 | Jane |
| 4 | Jack |
Task
| id | employee_id | description |
|----+-------------+---------------------|
| 1 | 1 | Fix bug |
| 2 | 1 | Implement feature |
| 3 | 1 | Deploy master |
| 4 | 2 | Integrate feature |
| 5 | 2 | Fix cosmetic issues |
Status
| id | task_id | time | details | Terminal |
|----+---------+-------+-----------+----------|
| 1 | 1 | 12:00 | Assigned | false |
| 2 | 1 | 12:30 | Started | false |
| 3 | 1 | 13:00 | Completed | true |
| 4 | 2 | 12:10 | Assigned | false |
| 5 | 2 | 14:00 | Started | false |
| 6 | 3 | 12:15 | Assigned | false |
| 7 | 4 | 12:20 | Assigned | false |
| 8 | 5 | 12:25 | Assigned | false |
| 9 | 4 | 12:30 | Started | false |
(I have also put these into a sqlfiddle page at http://sqlfiddle.com/#!9/728c85/1)
The basic idea is that I have some employees and tasks. The tasks can be assigned to employees and as they work on them, they keep adding "status" rows.
Each status entry has a field "terminal" which can either be true or false.
If the last status entry for a task has terminal set to true, then that task is over and there's nothing more to be done on it.
If all tasks assigned to an employee are over, then the employee is considered free.
I need to get a list of free employees. This would basically mean, given an employee, a list of all his or her tasks with statuses. So, something like this for Alice
| Task | Completed |
|-------------------+-----------|
| Fix bug | true |
| Implement feature | false |
| Deploy master | false |
From which I know that she's not free right now since there are 'false' entries in completed.
How would I do this? If my tables are not constructed properly for this kind of query, I'd very much like some advice on that too.
(I titled the question like this since I want to order the statuses of each task per user and them limit them to the last row).
Update
It was suggested to me that the status field should really go inside the tasks table and the Status table should simple be a log table.
I would go for the idea to have the status in the tasks table. (Please see my comment on your request on this.) However, here are two queries to select free employees:
If tasks cannot be re-opened, it is simple: Get all incompleted tasks by checking whether a record with terminal = true exists. Free employees are all that have no incomplete task.
select *
from employee
where id not in
(
select employee_id
from task
where id not in (select task_id from status where terminal = true)
);
If tasks can be re-opened, however, then you do the same but must find the last status. This can be done with Postgre's DISTINCT ON for instance.
select *
from employee
where id not in
(
select employee_id
from task
where not
(
select distinct on (task_id) terminal
from status
where task_id = task.id
order by task_id, id desc
)
);
(I am using the ID to find the last entry per task, as the time without a date seems inappropriate. You could only use the time column instead, if a task will always run within one day only.)
SQL fiddles:
http://sqlfiddle.com/#!15/f0ea8/2
http://sqlfiddle.com/#!15/f0ea8/1
You have to group all the statuses togheter and you can then use MAX() to find if one of them is true, like this:
SELECT t.description, MAX(s.terminal)
FROM Employee e
INNER JOIN task t ON t.employee_id = e.id
INNER JOIN status s ON s.task_id = t.id
GROUP BY t.id;
When you want this just for one user add something like this WHERE e.id = 1.
Hope this helps
select T.employee_id, T.description, S.Terminal
from Employee E
INNER JOIN Task T ON E.id=T.employee_id
INNER JOIN (Select task_id, max(id) as status_id FROM Status GROUP BY task_id) as ST on T.id=ST.task_id
INNER JOIN Status S on S.id=ST.status_id
I hope this will help you...??
select E.Name,T.id as[Task Id],T.description,S.Terminal from Employee E
inner join Task T on E.id=T.employee_id
inner join Status S on S.task_id=T.id
where e.id not in (select employee_id from Task where id in (select task_id from Status where Terminal='true' and details='Completed') )

Access SQL Select rows that have value in common with results of condition

Let's say I have a simple table, with the following format:
==================================
| ID | Invoice | Box | Delivered |
==================================
| 1 | AA11 | 1 | True |
----------------------------------
| 2 | AA11 | 2 | False |
----------------------------------
| 3 | AA22 | 1 | False |
----------------------------------
| 4 | AA33 | 1 | False |
----------------------------------
| 5 | AA44 | 1 | True |
----------------------------------
ID is a unique integer, Invoice is a TEXT field, Box is an Integer, and Delivered being a boolean (or BIT, as it's know in Access).
A query like this gets a list of everything that has been delivered:
SELECT * FROM Deliveries WHERE Delivered = True
However, each invoice can have multiple boxes (as is the case with invoice 1111), and sometimes not all the boxes are delivered at the same time. If a box has been delivered, I would like to be able to get the status of the other boxes with the same invoice number.
I know I can run multiple queries to do this. The one I mentioned above, and then another which loops through all the return results, and then runs another select with Invoice = ####.
Is there a way to do this all in a single query? I think it might be WHERE EXISTS, but I can't find out how to structure the query.
Ideally, I want the rows returned for a single query of the above able to be rows with ID's: 1, 2, and 5. This is the output I am looking for:
==================================
| ID | Invoice | Box | Delivered |
==================================
| 1 | 1111 | 1 | True |
----------------------------------
| 2 | 1111 | 2 | False |
----------------------------------
| 5 | 4444 | 1 | True |
----------------------------------
So that even though Delivered = False for ID 2, it is still returned because another item with the same invoice number has Delivered = True
When trying out some queries I got the error
Cannot join on memo, ole or hyperlink Object
Assuming you want something like this
select * from invoice where invoice in (SELECT invoice FROM invoice WHERE Delivered = 'True')
With the nested query your selecting an outputting the invoice numbers for reference in the parent query. Here the output of the nested query is used to 'filter' the results.
You already got it to work, but here is the another way, without changing the table.
SELECT invoice.ID, invoice.Invoice, invoice.Box, invoice.Delivered, invoice_1.Delivered AS Expr1
FROM invoice, invoice AS invoice_1
WHERE (((invoice.Invoice)=[invoice_1].[Invoice]) AND (([invoice_1].[Delivered])=Yes));
You can test it here.
For those getting into the same problem there is an explanation and a solution here
Apologies if I messed up the Access syntax:
select id
from T
group by id
having sum(iif(Delivered, 1, 0) < count(*)
Call the above query IC (incomplete). You may want to see all the undelivered data along with the ids:
select * from T
where id in (<IC>) and Delivered = false

Last accessed timestamp of a Netezza table?

Does anyone know of a query that gives me details on the last time a Netezza table was accessed for any of the operations (select, insert or update) ?
Depending on your setup you may want to try the following query:
select *
from _v_qryhist
where lower(qh_sql) like '%tablename %'
There are a collection of history views in Netezza that should provide the information you require.
Netezza does not track this information in the catalog, so you will typically have to mine that from the query history database, if one is configured.
Modern Netezza query history information is typically stored in a dedicated database. Depending on permissions, you may be able to see if history collection is enabled, and which database it is using with the following command. Apologies in advance for the screen-breaking wrap to come.
SYSTEM.ADMIN(ADMIN)=> show history configuration;
CONFIG_NAME | CONFIG_DBNAME | CONFIG_DBTYPE | CONFIG_TARGETTYPE | CONFIG_LEVEL | CONFIG_HOSTNAME | CONFIG_USER | CONFIG_PASSWORD | CONFIG_LOADINTERVAL | CONFIG_LOADMINTHRESHOLD | CONFIG_LOADMAXTHRESHOLD | CONFIG_DISKFULLTHRESHOLD | CONFIG_STORAGELIMIT | CONFIG_LOADRETRY | CONFIG_ENABLEHIST | CONFIG_ENABLESYSTEM | CONFIG_NEXT | CONFIG_CURRENT | CONFIG_VERSION | CONFIG_COLLECTFILTER | CONFIG_KEYSTORE_ID | CONFIG_KEY_ID | KEYSTORE_NAME | KEY_ALIAS | CONFIG_SCHEMANAME | CONFIG_NAME_DELIMITED | CONFIG_DBNAME_DELIMITED | CONFIG_USER_DELIMITED | CONFIG_SCHEMANAME_DELIMITED
-------------+---------------+---------------+-------------------+--------------+-----------------+-------------+---------------------------------------+---------------------+-------------------------+-------------------------+--------------------------+---------------------+------------------+-------------------+---------------------+-------------+----------------+----------------+----------------------+--------------------+---------------+---------------+-----------+-------------------+-----------------------+-------------------------+-----------------------+-----------------------------
ALL_HIST_V3 | NEWHISTDB | 1 | 1 | 20 | localhost | HISTUSER | aFkqABhjApzE$flT/vZ7hU0vAflmU2MmPNQ== | 5 | 4 | 20 | 0 | 250 | 1 | f | f | f | t | 3 | 1 | 0 | 0 | | | HISTUSER | f | f | f | f
(1 row)
Also make note of the CONFIG_VERSION, as it will come into play when crafting the following query example. In my case, I happen to be using the version 3 format of the query history database.
Assuming history collection is configured, and that you have access to the history database, you can get the information you're looking for from the tables and views in that database. These are documented here. The following is an example, which reports when the given table was the target of a successful insert, update, or delete by referencing the "usage" column. Here I use one of the history table helper functions to unpack that column.
SELECT FORMAT_TABLE_ACCESS(usage),
hq.submittime
FROM "$v_hist_queries" hq
INNER JOIN "$hist_table_access_3" hta
USING (NPSID, NPSINSTANCEID, OPID, SESSIONID)
WHERE hq.dbname = 'PROD'
AND hta.schemaname = 'ADMIN'
AND hta.tablename = 'TEST_1'
AND hq.SUBMITTIME > '01-01-2015'
AND hq.SUBMITTIME <= '08-06-2015'
AND
(
instr(FORMAT_TABLE_ACCESS(usage),'ins') > 0
OR instr(FORMAT_TABLE_ACCESS(usage),'upd') > 0
OR instr(FORMAT_TABLE_ACCESS(usage),'del') > 0
)
AND status=0;
FORMAT_TABLE_ACCESS | SUBMITTIME
---------------------+----------------------------
ins | 2015-06-16 18:32:25.728042
ins | 2015-06-16 17:46:14.337105
ins | 2015-06-16 17:47:14.430995
(3 rows)
You will need to change the digit at the end of the $v_hist_table_access_3 view to match your query history version.

Updating a field in a table with a number aggregated from other table

I have a log table with web log entries which have a session ID. I also have a session table summarizing sessions from the previous table. So I have to run some update SQL statement but I don't get how to construct a SQL statement for a field named "session_length". In this field I hope to assign the number of events in that particular session.
Let's say I have the following log table:
| Session ID | Timestamp | Action | ...
| 1 | 00:00:00 | get | ...
| 2 | 00:00:00 | get | ...
| 1 | 00:00:01 | get | ...
| 1 | 00:00:02 | get | ...
| 2 | 00:00:02 | get | ...
In the session table, I would like to have the following values for session_length field:
| Session ID | session_length | ...
| 1 | 3 | ...
| 2 | 2 | ...
I am not sure whether this can be done by a single query but I would like to see if this can be done by a single SQL query using update. In particular, I am using PostgresSQL in AWS RedShift.
You can do this with a correlated subquery in the update statement:
update sessions
set session_length = (select count(*)
from log
where log.sessionid = sessions.sessionid
)