This might be a trivial solution. I have searched similar posts regarding this but I couldn't find a proper solution.
I'm trying to delete a row if it exists in a table
I have a table say
Table1
----------------------------
|Database| Schema | Number |
----------------------------
| DB1 | S1 | 1 |
| DB2 | S2 | 2 |
| DB3 | S3 | 3 | <--- Want to delete this row
| DB4 | S4 | 4 |
----------------------------
Here is my query
DELETE FROM Table1
WHERE EXISTS
(SELECT * FROM Table1 WHERE Database = 'DB3' and Schema = 'S3');
When I tried the above SQL, it returned me an empty table, don't understand why it's returning an empty table.
There are similar posts on stack overflow but I couldn't find why I'm getting empty table.
Why are you using a subquery? Just use a where clause:
DELETE FROM Table1
WHERE Database = 'DB3' and Schema = 'S3';
Your code will delete either all rows or no rows. The where condition is saying "delete all rows from this table where this subquery returns at least one row". So, if the subquery returns one row, everything is deleted. Otherwise, nothing is deleted.
Imagine you have a table comments in your database.
The comment table has the columns, id, text, show, comment_id_no.
If a user enters a comment, it inserts a row into the database
| id | comment_id_no | text | show | inserted_at |
| -- | -------------- | ---- | ---- | ----------- |
| 1 | 1 | hi | true | 1/1/2000 |
If a user wants to update that comment it inserts a new row into the db
| id | comment_id_no | text | show | inserted_at |
| -- | -------------- | ---- | ---- | ----------- |
| 1 | 1 | hi | true | 1/1/2000 |
| 2 | 1 | hey | true | 1/1/2001 |
Notice it keeps the same comment_id_no. This is so we will be able to see the history of a comment.
Now the user decides that they no longer want to display their comment
| id | comment_id_no | text | show | inserted_at |
| -- | -------------- | ---- | ----- | ----------- |
| 1 | 1 | hi | true | 1/1/2000 |
| 2 | 1 | hey | true | 1/1/2001 |
| 3 | 1 | hey | false | 1/1/2002 |
This hides the comment from the end users.
Now a second comment is made (not an update of the first)
| id | comment_id_no | text | show | inserted_at |
| -- | -------------- | ---- | ----- | ----------- |
| 1 | 1 | hi | true | 1/1/2000 |
| 2 | 1 | hey | true | 1/1/2001 |
| 3 | 1 | hey | false | 1/1/2002 |
| 4 | 2 | new | true | 1/1/2003 |
What I would like to be able to do is select all the latest versions of unique commend_id_no, where show is equal to true. However, I do not want the query to return id=2.
Steps the query needs to take...
select all the most recent, distinct comment_id_nos. (should return id=3 and id=4)
select where show = true (should only return id=4)
Note: I am actually writing this query in elixir using ecto and would like to be able to do this without using the subquery function. If anyone can answer this in sql I can convert the answer myself. If anyone knows how to answer this in elixir then also feel free to answer.
You can do this without using a subquery using LEFT JOIN:
SELECT c.id, c.comment_id_no, c.text, c.show, c.inserted_at
FROM Comments AS c
LEFT JOIN Comments AS c2
ON c2.comment_id_no = c.comment_id_no
AND c2.inserted_at > c.inserted_at
WHERE c2.id IS NULL
AND c.show = 'true';
I think all other approaches will require a subquery of some sort, this would usually be done with a ranking function:
SELECT c.id, c.comment_id_no, c.text, c.show, c.inserted_at
FROM ( SELECT c.id,
c.comment_id_no,
c.text,
c.show,
c.inserted_at,
ROW_NUMBER() OVER(PARTITION BY c.comment_id_no
ORDER BY c.inserted_at DESC) AS RowNumber
FROM Comments AS c
) AS c
WHERE c.RowNumber = 1
AND c.show = 'true';
Since you have tagged with Postgresql you could also make use of DISTINCT ON ():
SELECT *
FROM ( SELECT DISTINCT ON (c.comment_id_no)
c.id, c.comment_id_no, c.text, c.show, c.inserted_at
FROM Comments AS c
ORDER By c.comment_id_no, inserted_at DESC
) x
WHERE show = 'true';
Examples on DB<>Fiddle
I think you want:
select c.*
from comments c
where c.inserted_at = (select max(c2.inserted_at)
from comments c2
where c2.comment_id_no = c.comment_id_no
) and
c.show = 'true';
I don't understand what this has to do with select distinct. You simply want the last version of a comment, and then to check if you can show that.
EDIT:
In Postgres, I would do:
select c.*
from (select distinct on (comment_id_no) c.*
from comments c
order by c.comment_id_no, c.inserted_at desc
) c
where c.show
distinct on usually has pretty good performance characteristics.
As I told in comments I don't advice to pollute data tables with history/auditory stuff.
And no: "double versioning" suggested by #Josh_Eller in his comment isn't a
good solution too: Not only for complicating queries unnecessarily but also for
being much more expensive in terms of processing and tablespace fragmentation.
Take in mind that UPDATE operations never update anything. They instead
write a whole new version of the row and mark the old one as deleted. That's
why vacuum processes are needed to defragment tablespaces in order to
recover that space.
In any case, apart of suboptimal, that approach forces you to implement more
complex queries to read and write data while in fact, I suppose most of the times you will only need to select, insert, update or even delete single row and only eventually, look its history up.
So the best solution (IMHO) is to simply implement the schema you actually need
for your main task and implement the auditory aside in a separate table and
maintained by a trigger.
This would be much more:
Robust and Simple: Because you focus on single thing every time (Single
Responsibility and KISS principles).
Fast: Auditory operations can be performed in an after trigger so
every time you perform an INSERT, UPDATE, or DELETE any possible lock
within the transaction is yet freed because the database engine knows that its outcome won't change.
Efficient: I.e. an update will, of course, insert a new row and mark
the old one as deleted. But this will be done at a low level by the database engine and, more than that: your auditory data will be fully unfragmented (because you only write there: never update). So the overall fragmentation would be always much less.
That being said, how to implement it?
Suppose this simple schema:
create table comments (
text text,
mtime timestamp not null default now(),
id serial primary key
);
create table comments_audit ( -- Or audit.comments if using separate schema
text text,
mtime timestamp not null,
id integer,
rev integer not null,
primary key (id, rev)
);
...and then this function and trigger:
create or replace function fn_comments_audit()
returns trigger
language plpgsql
security definer
-- This allows you to restrict permissions to the auditory table
-- because the function will be executed by the user who defined
-- it instead of whom executed the statement which triggered it.
as $$
DECLARE
BEGIN
if TG_OP = 'DELETE' then
raise exception 'FATAL: Deletion is not allowed for %', TG_TABLE_NAME;
-- If you want to allow deletion there are a few more decisions to take...
-- So here I block it for the sake of simplicity ;-)
end if;
insert into comments_audit (
text
, mtime
, id
, rev
) values (
NEW.text
, NEW.mtime
, NEW.id
, coalesce (
(select max(rev) + 1 from comments_audit where id = new.ID)
, 0
)
);
return NULL;
END;
$$;
create trigger tg_comments_audit
after insert or update or delete
on public.comments
for each row
execute procedure fn_comments_audit()
;
And that's all.
Notice that in this approach you will have always your current comments data
in comments_audit. You could have instead used the OLD register and only
define the trigger in the UPDATE (and DELETE) operations to avoid it.
But I prefer this approach not only because it gives us an extra redundancy (an
accidental deletion -in case it were allowed or the trigger where accidentally
disabled- on the master table, then we would be able to recover all data from
the auditory one) but also because it simplifies (and optimises) querying the
history when it's needed.
Now you only need to insert, update or select (or even delete if you develop a little more this schema, i.e. by inserting a row with nulls...) in a fully transparent manner just like if it weren't any auditory system. And, when you need that data, you only need to query the auditory table instead.
NOTE: Additionally you could want to include a creation timestamp (ctime). In this case it would be interesting to prevent it of being modified in a BEFORE trigger so I omitted it (for the sake of simplicity again) because you can already guess it from the mtimes in the auditory table (even if you are going to use it in your application it would be very advisable to add it).
If you are running Postgres 8.4 or higher, ROW_NUMBER() is the most efficient solution :
SELECT *
FROM (
SELECT c.*, ROW_NUMBER() OVER(PARTITION BY comment_id_no ORDER BY inserted_at DESC) rn
FROM comments c
WHERE c.show = 'true'
) x WHERE rn = 1
Else, this could also be achieved using a WHERE NOT EXISTS condition, that ensures that you are showing the latest comment :
SELECT c.*
FROM comments c
WHERE
c.show = 'true '
AND NOT EXISTS (
SELECT 1
FROM comments c1
WHERE c1.comment_id_no = c.comment_id_no AND c1.inserted_at > c.inserted_at
)
You have to use group by to get the latest ids and the join to the comments table to filter out the rows where show = false:
select c.*
from comments c inner join (
select comment_id_no, max(id) maxid
from comments
group by comment_id_no
) g on g.maxid = c.id
where c.show = 'true'
I assume that the column id is unique and autoincrement in comments table.
See the demo
I have two sqlite3 tables with same column names and I want to compare them. To do that, I need to join the tables and juxtapose the columns with same name.
The tables share an identical column which I want to put as the first column.
Let's imagine I have table t1 and table t2
Table t1:
SharedColumn | Height | Weight
A | 2 | 70
B | 10 | 100
Table t2:
SharedColumn | Height | Weight
A | 5 | 25
B | 32 | 30
What I want get as a result of my query is :
SharedColumn | Height_1 | Height_2 | Weight_1 | Weight_2
A | 2 | 5 | 70 | 25
B | 10 | 32 | 100 | 30
In my real case i have a lot of columns so I would like to avoid writing each column name twice to specify the order.
Renaming the columns is not my main concern, what interests me the most is the juxtaposition of columns with same name.
There is no way to do that directly in SQL especially because you also want to rename the columns to identify their source, you'll have to use dynamic SQL and honestly? Don't! .
Simply write the columns names, most SQL tools provide a way to generate the select, just copy them and place them in the correct places :
SELECT t1.sharedColumn,t1.height as height_1,t2.height as height_2 ...
FROM t1
JOIN t2 ON(t1.sharedColumn = t2.sharedColumn)+
Try the following query to get the desired result!!
SELECT t1.Height AS Height_1, t1.Weight AS Weight_1, t1.sharedColumn AS SharedColumn
t2.Height AS Height_2, t2.Weight AS Weight_2
FROM t1 INNER JOIN t2
ON t1.sharedColumn = t2.sharedColumn
ORDER By t1.sharedColumn ASC
After that, you can fetch the result by following lines:
$result['SharedColumn'];
$result['Height_1'];
$result['Height_2'];
$result['Weight_1'];
$result['Weight_1'];
I have a table with 600 000+ rows called asset. The customer has added a new column and would like it populated with a value from another table:
ASSET TEMP
| id | ... | newcol | | id | condition |
--------------------- ------------------
|0001| ... | - | |0001| 3 |
If I try to update it all at once, it times out/claims there is a dead lock:
update asset set newcol = (
select condition from temp where asset.id = temp.id
) where newcol is null;
The way I got around it was by only doing a 100 rows at a time:
update (select id, newcol from asset where newcol is null
fetch first 100 rows only) a1
set a1.newcol = (select condition from temp a2 where a1.id = a2.id);
At the moment I am making good use of the copy/paste utility, but I'd like to know of a more elegant way to do it (as well as a faster way).
I have tried putting it in a PL/SQL loop but I can't seem to get it to work with DB2 as a standalone script.
I'm trying to create a View in SQL Server 2008 R2, where data is extracted from two tables with dual one-to-one relationships, and I want to create two columns in the view based values from a single column in one of the tables.
The tables are currently similar to these examples:
TableA:
PrimaryKey | Name | Value_FK | Number_FK
-------------------------------------------
66 | NameA | 1 | 2
77 | NameB | 3 | 4
TableB:
PrimaryKey | Value
-------------------
1 | 238
2 | 456
3 | 100
4 | 200
The View should look like this:
Name | Value | Number
-------------------------
NameA | 238 | 456
NameB | 100 | 200
('Value' and 'Number' are essentially the same type, and are both found in the 'Value' column in TableB. I thought it would be more easy to distingiush between 'Value' and 'Number', than 'ValueA' and 'ValueB').
The factor that should decide which values go into Column 'Value' or Column 'Number' is the PrimaryKey in TableB and its references in either foreignkey in TableA (but both FKs shall NEVER refer to the same key).
This is likely not the most brilliant database model, having dual relationships between to tables. This is however due to mapping some C#.NET classes to a database by using ADO.NET Entity Framework, where Class A has two objects of Class B (in this case named 'Value' and 'Number'), and the database model currently constructs two relationships becuase of this. Changing this is not an option.
I've tried googling this, but I find it difficult to find an answer that I need. Especially when most results are about the opposite: Selecting multiple columns into one column.
So, how should I write the Select statement?
CREATE VIEW ViewName
AS
SELECT DISTINCT a.Name as 'Name', ????? as 'Value', ????? as 'Number'
FROM TableA a, TableB b
I am quite rusty with advanced SQL-commands. It's been over 1,5 years since last I was into something this advanced. I tried something similar to this first:
CREATE VIEW ViewName
AS
WITH Name AS
( SELECT DISTINCT a.Name FROM TableA a )
Value AS
(
SELECT DISTINCT b.Value as 'Value' FROM TableA a, TableB b
WHERE b.PrimaryKey = an.ValueA_FK
),
Number AS
(
SELECT DISTINCT b.Value as 'Number'
FROM TableA a, TableB b
WHERE a.PrimaryKey = an.ValueB_PrimaryKey
)
SELECT DISTINCT
* FROM Name, Value, Number
The result of my utterly failed attempt is like this:
Name | Value | Number
-------------------------
NameA | 100 | 200
NameB | 100 | 200
NameA | 100 | 456
NameB | 100 | 456
NameA | 238 | 200
NameB | 238 | 200
NameA | 238 | 456
NameB | 238 | 456
Now, any suggestiong as what to fill in the query?
You can reference the same table more than once in the FROM clause:
SELECT a.Name as 'Name', b1.Value as 'Value', b2.Value as 'Number'
FROM TableA a
inner join TableB b1
on
a.Value_FK = b1.PrimaryKey
inner join TableB b2
on
a.Number_FK = b2.PrimaryKey
I've also removed the DISTINCT, since it shouldn't be your habit to add one, and there's nothing in the question that suggests one is necessary. I've also used ANSI-Style joins. These are almost always to be preferred over the older style (where tables in the FROM clause are just separated by commas)
If it's possible that some rows in TableA have a NULL Value_FK or Number_FK, and you still want those rows in your view, you would switch one or both of the inner joins to be left joins. You would then also decide whether the output column should be NULL (in which case you're done) or some other value (in which case, you would have e.g. COALESCE(b1.Value,<Value when null>) as 'Value').
This query will yield results you want:
select t1.Name, value.Value, number.Value as Number
from TableA t1
inner join TableB value on value.PrimaryKey = t1.Value_FK
inner join TableB number on number.PrimaryKey = t1.Number_FK
you over-complicated your query a lot.