UPDATE statement in SQLite with nested FROM and JOIN - sql

I currently have an SQL statment:
UPDATE table_1 SET
property_1=b.value_1,
property_2=b.value_2,
property_3=b.value_3
FROM (
SELECT a.property_4, a.property_5, b.value_2, b.value_3
FROM (
SELECT id1 AS property_4, MAX(id2) AS property_5
FROM table_2
WHERE
id1 IN (...) AND
id2 NOT IN (...)
) a
JOIN table_3 b ON
a.property_5 = b.id
) a
WHERE
table_1.id = a.property_4
which works fine on our production postgresql db, however the syntax for UPDATE is different in SQLite (what we use in test) and I am finding my self quite stuck as to how to convert it. The error I receive is Error: syntax error near FROM. If anyone is a SQLite whiz I would greatly appreciate some guidance.

Since SQLite doesn't support UPDATE with JOIN/FROM clause. You can use CTE & SubQuery to do it alternatively:
WITH cte AS (
SELECT a.property_4, b.value_1, b.value_2, b.value_3
FROM (
SELECT id1 AS property_4, MAX(id2) AS property_5
FROM table_2
WHERE
id1 IN (...) AND
id2 NOT IN (...)
) a
JOIN table_3 b ON
a.property_5 = b.id
)
UPDATE table_1 SET
property_1=(select value_1 from cte where cte.property_4 = id)
property_2=(select value_2 from cte where cte.property_4 = id)
property_3=(select value_3 from cte where cte.property_4 = id)
WHERE
id IN (select property_4 from cte)

Related

SQL to search and delete with several keycolumns

A bit bad title maybe but I try to explain what I mean.
I use SQL like this to build a list
SELECT id
FROM parcel
WHERE id IN (113715, 113824, 113855, 113954, 114010, 114116, 114272, 114329)
where ID is a column in parcel table that is quarantined to be unique, very straightforward.
But some tables use many columns to be unique.
SELECT id1, id2
FROM trip
WHERE id1, id2 IN ((113715, 113824), (113855, 113954), (114010, 114116),(114272, 114329))
The last SQL is obviously not working.
I want to select rows where
id1 = 113715 and id2=113824 or
id1 = 113855 and id2=113954 or
id1 = 114010 and id2=114116 or
id1 = 114272 and id2=114329
In reality the generated SQL can contain 500 - 1000 ids.
What SQL should I use ?
EDIT
This is a generated SQL. The ID's come from another database on another server so JOIN for example is not possible.
SELECT id1, id2 FROM trip
INTERSECT
select id1, id2 from (values (113715,113824),(113855,113954),(114010,114116),(114272,114329)) a(id1,id2)
USE INTERSECT
You could use a join with a values list:
SELECT id1, id2
FROM trip
INNER JOIN (VALUES (113715,113824),(113855,113954),(114010,114116),(114272,114329)) V(a, b)
ON id1 = a
AND id2 = b
Can you use the generated ids like this?
;WITH GenerateIds AS
(
SELECT * FROM (VALUES
-- Paste the Id list below
(113715,113824),(113855,113954),(114010,114116),(114272,114329)
) t(id1, id2)
)
-- Use inner join
SELECT id1, id2 FROM trip INNER JOIN GenerateIds g ON
trip.id1 = g.id1 AND trip.id2 = g.id2
Unfortunately SQL Server doesn't support tuples in IN like
SELECT id1, id2
FROM trip
WHERE (id1,id2) in ((113715,113824),(113855,113954),(114010,114116),(114272,114329));
which is valid in some other DBMS.
So you can either follow your own suggestion
SELECT id1, id2
FROM trip
WHERE (id1 = 113715 and id2 = 113824)
OR (id1 = 113855 and id2 = 113954)
OR (id1 = 114010 and id2 = 114116)
...;
Or use a temporary table, cte or the like. Easiest might be to join a values clause:
SELECT trip.id1, trip.id2
FROM trip
JOIN (VALUES (113715,113824),(113855,113954),(114010,114116),(114272,114329))
AS src(id1, id2) ON src.id1 = trip.id1 AND src.id2 = trip.id2;
Another Simple Query:
SELECT id1, id2
FROM trip
WHERE 1 = 1
AND ((id1 = 113715 and id2=113824)
OR (id1 = 113855 and id2=113954)
OR (id1 = 114010 and id2=114116) OR (id1 = 114272 and id2=114329))

SQL Server 2012 writing duplicate entries into table from CTE

So I am writing to a table the output from a few sequential CTEs, and when I fixed a join in one of the CTEs from an inner to a left join, there are now duplicated entries in the Table that do not show up if I just run the query without the insert.
Is there something I need to understanding about creating and inserting into a table with regards to joins in a CTE?
EDIT
create table MYTABLE
(
ID int,
Date smalldatetime,
Val1 int,
Val2 int
)
; with cte1 as (
select
a.ID,
a.Date,
a.Val1,
b.Val2
from table1 a
left join table2 b
on a.ID = b.ID
and a.Date = b.Date
)
insert into MYTABLE
(ID, Date, Val1, Val2)
select * from cte1
When creating the table on the inner join there is no problem with duplicates; on the left join (as shown above), rows where there are NULLs appear to be duplicated many times.
Check your right table (table2) my guess is that there are more than one record that have the same ID and Date.
If that is the case, the records are not technically duplicated if you do a select all (*) in the CTE, you will see the other fields that have changed.
If you do not care about the rest of the fields being different though, just try adding a Row_Number to your CTE and select where the Row_Number = 1 outside of the CTE.
For Instance:
create table MYTABLE
(
ID int,
Date smalldatetime,
Val1 int,
Val2 int
)
; with cte1 as (
select
a.ID,
a.Date,
a.Val1,
b.Val2
Rnum = ROW_NUMBER() OVER(PARTITION BY a.ID, a.Date, a.Val1, a.Val2 ORDER BY ID)
from table1 a
left join table2 b
on a.ID = b.ID
and a.Date = b.Date
)
insert into MYTABLE
(ID, Date, Val1, Val2)
select ID, Date, Val1, Val2 from cte1
where Rnum = 1
The row_number acts as a "distinct" and depending on what combination of fields you want to not duplicate, you will get different results.
For instance, if you do not want the IDs to duplicate, then
Rnum = ROW_NUMBER() OVER(PARTITION BY a.ID ORDER BY ID)
if you do not care about the IDs duplicating, but you do not want the same ID on the same date, then
Rnum = ROW_NUMBER() OVER(PARTITION BY a.ID, a.Date ORDER BY ID)
etc.... just depends on your selection criteria of what you do not want to duplicate.
Hope this helps

sql to combine two unrelated tables into one

I have tables
table1
col1 col2
a b
c d
and table2
mycol1 mycol2
e f
g h
i j
k l
I want to combine the two tables, which have no common field into one table looking like:
table 3
col1 col2 mycol1 mycol2
a b e f
c d g h
null null i j
null null k l
ie, it is like putting the two tables side by side.
I'm stuck! Please help!
Get a row number for each row in each table, then do a full join using those row numbers:
WITH CTE1 AS
(
SELECT ROW_NUMBER() OVER(ORDER BY col1) AS ROWNUM, * FROM Table1
),
CTE2 AS
(
SELECT ROW_NUMBER() OVER (ORDER BY mycol1) AS ROWNUM, * FROM Table2
)
SELECT col1, col2, mycol1, mycol2
FROM CTE1 FULL JOIN CTE2 ON CTE1.ROWNUM = CTE2.ROWNUM
This is assuming SQL Server >= 2005.
It's really good if you put in a description of why this problem needs to be solved. I'm guessing it is just to practice sql syntax?
Anyway, since the rows don't have anything connecting them, we have to create a connection. I chose the ordering of their values. Also since they have nothing connecting them that also begs the question on why you would want to put them next to each other in the first place.
Here is the complete solution: http://sqlfiddle.com/#!6/67e4c/1
The select code looks like this:
WITH rankedt1 AS
(
SELECT col1
,col2
,row_number() OVER (order by col1,col2) AS rn1
FROM table1
)
,rankedt2 AS
(
SELECT mycol1
,mycol2
,row_number() OVER (order by mycol1,mycol2) AS rn2
FROM table2
)
SELECT
col1,col2,mycol1,mycol2
FROM rankedt1
FULL OUTER JOIN rankedt2
ON rn1=rn2
Option 1: Single Query
You have to join the two tables, and if you want each row in table1 to match to only one row in table2, you have to restrict the join somehow. Calculate row numbers in each table and join on that column. Row numbers are database-specific; here is a solution for mysql:
SELECT
t1.col1, t1.col2, t2.mycol1, t2.mycol2
FROM
(SELECT col1, col2, #t1_row := t1_row + 1 AS rownum FROM table1, (SELECT #t1_row := 0) AS r1) AS t1
LEFT JOIN
(SELECT mycol1, mycol2, #t2_row := t2_row + 1 AS rownum FROM table2, (SELECT #t2_row := 0) AS r2) AS t2
ON t1.rownum = t2.rownum;
This assumes table1 is longer than table2; if table2 is longer, either use RIGHT JOIN or switch the order of the t1 and t2 sub-selects. Also note that you can specify the order of each table separately using an ORDER BY clause in the sub-selects.
(See select increment counter in mysql)
Option 2: Post-processing
Consider making two selects, and then concatenating the results with your favorite scripting language. This is a much more reasonable approach.

SQL - using a value in a nested select

Hope the title makes some kind of sense - I'd basically like to do a nested select, based on a value in the original select, like so:
SELECT MAX(iteration) AS maxiteration,
(SELECT column
FROM table
WHERE id = 223652
AND iteration = maxiteration)
FROM table
WHERE id = 223652;
I get an ORA-00904 invalid identifier error.
Would really appreciate any advice on how to return this value, thanks!
It looks like this should be rewritten with a where clause:
select iteration,
col
from tbl
where id = 223652
and iteration = (select max(iteration) from tbl where id = 223652);
You can circumvent the problem alltogether by placing the subselect in an INNER JOIN of its own.
SELECT t.iteration
, t.column
FROM table t
INNER JOIN (
SELECT id, MAX(iteration) AS iteration
FROM table
WHERE id = 223652
) tm ON tm.id = t.id AND tm.iteration = t.iteration
Since you're using Oracle, I'd suggest using analytic functions for this:
SELECT * FROM (
SELECT col,
iteration,
row_number() over (partition by id order by iteration desc) rn
FROM tab
WHERE id = 223652
) WHERE rn = 1
do it like this:
with maxiteration as
(
SELECT MAX(iteration) AS maxiteration
FROM table
WHERE id = 223652
)
select
column,
iteration
from
table
where
id = 223652
AND iteration = maxiteration
;
Not 100% sure on Oracle syntax, but isn't it something like:
select iteration, column from table where id = 223652 order by iteration desc limit 1
I would approach this problem in a slightly different way. You're basically looking for the row that has no other iterations greater than it. There are at least 3 ways I can think of to do this:
SELECT
T1.iteration AS maxiteration,
T1.column
FROM
Table T1
WHERE
T1.id = 223652 AND
NOT EXISTS
(
SELECT *
FROM Table T2
WHERE
T2.id = 223652 AND
T2.iteration > T1.iteration
)
Or...
SELECT
T1.iteration AS maxiteration,
T1.column
FROM
Table T1
LEFT OUTER JOIN Table T2 ON
T2.id = T1.id AND
T2.iteration > T1.iteration
WHERE
T1.id = 223652 AND
T2.id IS NULL
Or...
SELECT
T1.iteration AS maxiteration,
T1.column
FROM
Table T1
INNER JOIN (SELECT id, MAX(iteration) AS maxiteration FROM Table T2 GROUP BY id) SQ ON
SQ.id = T1.id AND
SQ.maxiteration = T1.iteration
WHERE
T1.id = 223652
EDIT: I didn't see the ORA error the first time reading the question and it wasn't tagged as Oracle specific. I think that there may be some differences in the syntax and use of aliases in Oracle, so you may need to tweak some of the above queries.
The Oracle error is telling you that it doesn't know what maxiteration is, because the column alias isn't available yet inside the subquery. You need to refer to it by the table alias and column name instead of the column alias I believe.
You do something like
select maxiteration,column from table a join (select max(iteration) as maxiteration from table where id=1) b using (id) where b.maxiteration=a.iteration;
This could of course return multiple rows for one maxiteration unless your table has a constraint against it.

SQL Select Distinct with Conditional

Table1 has columns (id, a, b, c, group). There are several rows that have the same group, but id is always unique. I would like to SELECT group,a,b FROM Table1 WHERE the group is distinct. However, I would like the returned data to be from the row with the greatest id for that group.
Thus, if we have the rows
(id=10, a=6, b=40, c=3, group=14)
(id=5, a=21, b=45, c=31, group=230)
(id=4, a=42, b=65, c=2, group=230)
I would like to return these 2 rows:
[group=14, a=6,b=40] and
[group=230, a=21,b=45] (because id=5 > id=4)
Is there a simple SELECT statement to do this?
Try:
select grp, a, b
from table1 where id in
(select max(id) from table1 group by grp)
You can do it using a self join or an inner-select. Here's inner select:
select `group`, a, b from Table1 AS T1
where id=(select max(id) from Table1 AS T2 where T1.`group` = T2.`group`)
And self-join method:
select T1.`group`, T2.a, T2.b from
(select max(id) as id,`group` from Table1 group by `group`) T1
join Table1 as T2 on T1.id=T2.id
2 selects, your inner select gets:
SELECT MAX(id) FROM YourTable GROUP BY [GROUP]
Your outer select joins to this table.
Think about it logically, the inner select gets a sub set of the data you need.
The outer select inner joins to this subset and can get further data.
SELECT [group], a, b FROM YourTable INNER JOIN
(SELECT MAX(id) FROM YourTable GROUP BY [GROUP]) t
ON t.id = YourTable.id
SELECT mi.*
FROM (
SELECT DISTINCT grouper
FROM mytable
) md
JOIN mytable mi
ON mi.id =
(
SELECT id
FROM mytable mo
WHERE mo.grouper = md.grouper
ORDER BY
id DESC
LIMIT 1
)
If your table is MyISAM or id is not a PRIMARY KEY, then make sure you have a composite index on (grouper, id).
If your table is InnoDB and id is a PRIMARY KEY, then a simple index on grouper will suffice (id, being a PRIMARY KEY, will be implictly included).
This will use an INDEX FOR GROUP-BY to build the list of distinct groupers, and for each grouper it will use the index access to find the maximal id.
Don't know how to do it in mysql. But the following code will work for MsSQL...
SELECT Y.* FROM
(
SELECT DISTINCT [group], MAX(id) ID
FROM Table1
GROUP BY [group]
) X
INNER JOIN Table1 Y ON X.ID=Table1.ID