I have a MySQL query that works on my current MySQL database. I've been forced to move over to oracle, so I'm trying to port all my stored procedures / programs to use the Oracle SQL Syntax. I'm having a lot of trouble on one particular query. Here is the MySQL query. It updates a table using a subquery.
update table1 alf
set nextcontractid =
(
select
contractid from table1copy alf2
where
alf2.assetid = alf.AssetID
and
alf2.lasttradedate > alf.LastTradeDate
order by lasttradedate asc limit 1
)
where complete = 0
In oracle, I can't use the the limit command, so I've looked for the workaround. Here is my oracle query. (which doesn't work.)
update table1 alf
set nextcontractid =
(select contractid from
(
SELECT contractid, rownum as row_number
FROM table1copy alf2
WHERE alf2.assetid = alf.assetid
AND alf2.lasttradedate > alf.lasttradedate
ORDER BY lasttradedate ASC
)
where row_number = 1)
where alf.complete = 0
I get the following error:
Error at Command Line:8 Column:29
Error report:
SQL Error: ORA-00904: "ALF"."LASTTRADEDATE": invalid identifier
00904. 00000 - "%s: invalid identifier"
line 8 is:
AND alf2.lasttradedate > alf.lasttradedate
Removing the update statement and putting in some dummy values into the subquery yields the correct results for the subquery:
(select contractid from
(
SELECT contractid, rownum as row_number
FROM asset_list_futures_copy alf2
WHERE alf2.assetid = 'GOLD'
AND alf2.lasttradedate > '20110101'
ORDER BY lasttradedate ASC
)
where row_number = 1)
Looking at the error, it looks like the second reference to alf isn't working. Any idea how I can change my query so that it works in oracle?
Seems the parser does not like that, despite the fact it is sintacticaly correct. Probably the two imbricated and ordered clause is blinding him somehow. I reproduced that.
You can use an analytical function:
update table1 alf
set nextcontractid =
(SELECT min(contractid) keep (dense_rank first order by lasttradedate asc)
FROM table1copy alf2
WHERE alf2.assetid = alf.assetid
AND alf2.lasttradedate > alf.lasttradedate
)
where alf.complete = 0
You can use WHERE rownum = 1, or rownum BETWEEN x AND y in cases that you want more results.
Related
I have a following SQL which works in mysql version 5.6 but is breaking in mysql version 5.7.x.
SELECT * FROM (SELECT * FROM photos WHERE photoable_type = 'Mobilehome'
AND photoable_id IN (SELECT id FROM mobilehomes WHERE
mobilehomes.community_id = 1) AND photos.image_file_size IS NOT NULL
AND photos.is_published IS TRUE ORDER BY photos.priority ASC) AS tmp_table
GROUP BY photoable_id
It's throwing me following error:
Expression #1 of SELECT list is not in GROUP BY clause and contains
nonaggregated column 'tmp_table.id' which is not functionally
dependent on columns in GROUP BY clause; this is
incompatible with sql_mode=only_full_group_by
In this case or you change the sql mode for instrcut the db to work as mysql 5.6 version or you can adeguate your query to the new behavior
In this second case
If you use group by whithout aggregation function this mean that for all the column different from photoable_id you accept casual result
This mean that you could, probably, also accepted an aggregated result based greagtion function eg: on min() or max ()
assuming your tables containg col1, col2, .. the you must declare explicitally the column you need
SELECT photos.photoable_id, min(col1), min(col2),....
FROM photos
INNER JOIN mobilehomes ON mobilehomes.community_id = 1
AND photos.photoable_type = 'Mobilehome'
AND photos.photoable_id = mobilehomes.id
AND photos.image_file_size IS NOT NULL
AND photos.is_published IS TRUE
GROUP BY photos.photoable_id
ORDER BY photos.priority ASC
Looking to your code seems also that you could avoid the subquery
I have quite a complex query to essentially select the cheapest delivery service price per delivery service.
In order to get unique records per delivery service, I utilise the DISTINCT function in SQL. This query provides correct results:
DeliveryServicePrice.active.select('DISTINCT ON (delivery_service_id) *').order('delivery_service_id, price ASC')
(only a part of the query)
However, this query only seems to work with PostgreSQL (which I think is strange considering PostgreSQL is a lot more strict with SQL standards); it does not work with MySQL and SQLite. I receive the following error:
Mysql2::Error: You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version for the right syntax to
use near 'ON (delivery_service_id) * FROM `delivery_service_prices`
WHERE `delivery_servi' at line 1: SELECT DISTINCT ON
(delivery_service_id) * FROM `delivery_service_prices` WHERE
`delivery_service_prices`.`active` = 1 AND (2808.0 >= min_weight AND
2808.0 <= max_weight AND 104.0 >= min_length AND 104.0 <= max_length AND 104.0 >= min_thickness AND 104.0 <= max_thickness) ORDER BY delivery_service_id, price ASC
The application I'm building is open source, so it's required to support all 3 database types.
How do I create DISTINCT ON queries for MySQL and SQLite in the Rails framework syntax?
I'm using Rails 4.1.
Resources
My previous problem for reference:
How to select unique records based on foreign key column in Rails?
File and line number for where the query is being used.
Finished answer
DeliveryServicePrice.select('delivery_service_prices.id').active.joins('LEFT OUTER JOIN delivery_service_prices t2 ON (delivery_service_prices.delivery_service_id = t2.delivery_service_id AND delivery_service_prices.price > t2.price)').where('t2.delivery_service_id IS NULL')
DISTINCT ON is a Postgres specific extension to the standard SQL DISTINCT. Neither of them is a "function", both are SQL key words - even though the parentheses required after DISTINCT ON make it look like a function.
There are a couple of techniques to rewrite this with standard-SQL, all of them more verbose, though. Since MySQL does not support window-functions row_number() is out.
Details and more possible query techniques:
Select first row in each GROUP BY group?
Fetch the row which has the Max value for a column
Rewritten with NOT EXISTS:
SELECT *
FROM delivery_service_prices d1
WHERE active = 1
AND 2808.0 BETWEEN min_weight AND max_weight
AND 104.0 BETWEEN min_length AND max_length
AND 104.0 BETWEEN min_thickness AND max_thickness
AND NOT EXISTS (
SELECT 1
FROM delivery_service_prices d2
WHERE active = 1
AND 2808.0 BETWEEN min_weight AND max_weight
AND 104.0 BETWEEN min_length AND max_length
AND 104.0 BETWEEN min_thickness AND max_thickness
AND d2.delivery_service_id = d1.delivery_service_id
AND d2.price < d1.price
AND d2.<some_unique_id> < d1.<some_unique_id> -- tiebreaker!
)
ORDER BY delivery_service_id
If there can be multiple rows with the same price for the same delivery_service_id, you need to add some unique tie-breaker to avoid multiple results per delivery_service_id. At least if you want a perfectly equivalent query. My example would select the row with the smallest <some_unique_id> from each set of dupes.
Unlike with DISTINCT ON, ORDER BY is optional here.
DeliveryServicePrice.active.select(:delivery_service_id).distinct.order('delivery_service_id, price ASC')
I have a script that checks for duplicate pairs in a database and selects all entries that need to be deleted except for one.
I have this script that selects the first 100 entries that need to be deleted and works fine:
SELECT*
FROM vl_posts_testing
INNER JOIN (
SELECT max(ID) AS lastId, `post_content`,`post_title`
FROM vl_posts_testing WHERE vl_posts_testing.post_type='post'
GROUP BY `post_content`,`post_title`
HAVING count(*) > 1) duplic
ON duplic.`post_content` = vl_posts_testing.`post_content`
AND duplic.`post_title` = vl_posts_testing.`post_title`
WHERE vl_posts_testing.id < duplic.lastId
AND vl_posts_testing.post_type='post'
LIMIT 0,100
However when I try to delete this set of data using:
DELETE vl_posts_testing
FROM vl_posts_testing
INNER JOIN (
SELECT max(ID) AS lastId, `post_content`,`post_title`
FROM vl_posts_testing WHERE vl_posts_testing.post_type='post'
GROUP BY `post_content`,`post_title`
HAVING count(*) > 1) duplic
ON duplic.`post_content` = vl_posts_testing.`post_content`
AND duplic.`post_title` = vl_posts_testing.`post_title`
WHERE vl_posts_testing.id < duplic.lastId
AND vl_posts_testing.post_type='post'
LIMIT 100
I receive the fallowing error:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'LIMIT 10' at line 8
The script has been constructed using this answer https://stackoverflow.com/a/6108860/1168944
Actually the script works just fine on a small amount of data without the limits set, however due to the fact that I run it on a big table (some 600k entries) I need to split this script in a routine that processes only a limited amount of data due to server limits like processor, memory etc.
Also took into consideration this example: MySQL LIMIT on DELETE statement but the result is different since no modification is executed no matter how small is the limit.
After several retries I have found a way to make it work:
DELETE vl_posts_testing
FROM vl_posts_testing
INNER JOIN (
SELECT max(ID) AS lastId, `post_content`,`post_title`
FROM vl_posts_testing WHERE vl_posts_testing.post_type='post'
GROUP BY `post_content`,`post_title`
HAVING count(*) > 1
LIMIT 0,100 ) duplic
ON duplic.`post_content` = vl_posts_testing.`post_content`
AND duplic.`post_title` = vl_posts_testing.`post_title`
WHERE vl_posts_testing.id < duplic.lastId
AND vl_posts_testing.post_type='post'
Actually what I did is set an inner limit to the first set of data and compare it to the rest of the database in order to make it work. It work but I am not sure this is the correct way ot do it.
I put together a test table for a error I recently came across. It involves the use of LIMIT when attempting to delete a single record from a MySQL table.
The error I speak of is "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 1' at line 1"
The table I put together is called test; it has 3 columns, id, name and created. I populated the table with several records and then attempted to delete one. Below is the statement I used to try and accomplish this.
DELETE t FROM test t WHERE t.name = 'foo' LIMIT 1
Without the use of LIMIT 1, the statement executes just fine, but of course I wouldn't be using LIMIT if there wasn't a need for it.
I'm fully aware that I can use another statement to accomplish this DELETE successfully. See below:
DELETE FROM test WHERE name = 'foo' LIMIT 1
However my question is centered on why the first statement isn't working with LIMIT.
So my question is, what I have done incorrectly with respect to the first statement to generate this error?
simply use
DELETE FROM test WHERE 1= 1 LIMIT 10
the delete query only allows for modifiers after the DELETE 'command' to tell the database what/how do handle things.
see this page
From the documentation:
You cannot use ORDER BY or LIMIT in a multiple-table DELETE.
DELETE t.* FROM test t WHERE t.name = 'foo' LIMIT 1
#Andre If I understood what you are asking, I think the only thing missing is the t.* before FROM.
Use row_count - your_desired_offset
So if we had 10 rows and want to offset 3
10 - 3 = 7
Now the query delete from table where this = that order asc limit 7 keeps the last 3, and order desc to keep the first 3:
$row_count - $offset = $limit
Delete from table where entry = criteria order by ts asc limit $limit
There is a workaround to solve this problem by using a derived table.
DELETE t1 FROM test t1 JOIN (SELECT t.id FROM test LIMIT 1) t2 ON t1.id = t2.id
Because the LIMIT is inside the derived table the join will match only 1 row and thus the query will delete only this row.
First I struggled a bit with a
DELETE FROM ... USING ... WHERE query,...
Since i wanted to test first
so i tried with SELECT FROM ... USING... WHERE ...
and this caused an error , ...
Then i wanted to reduce the number of deletions adding
LIMIT 10
which also produced an error
Then i removed the "LIMIT" and - hurray - it worked:
"1867 rows deleted. (Query took 1.3025 seconds.)"
The query was:
DELETE FROM tableX
USING tableX , tableX as Dup
WHERE NOT tableX .id = Dup.id
AND tableX .id > Dup.id
AND tableX .email= Dup.email
AND tableX .mobil = Dup.mobil
This worked.
I am trying to return a number based on the count of results from a table and to avoid having to count the results twice in the IF statement I am using a subquery. However I get a syntax error when trying to run the query, the subquery I have tested by itself runs fine.
Any ideas what is wrong with the query? The syntax looks correct to me
SELECT IF(daily_count>8000,0,IF(daily_count>6000,1,2))
FROM (
SELECT count(*) as daily_count
FROM orders201003
WHERE DATE_FORMAT(date_sub(curdate(), INTERVAL 1 DAY),"%d-%m-%y") =
DATE_FORMAT(reqDate,"%d-%m-%y")
) q
Error message I get is:
You have an error in your SQL syntax.
Check the manual that corresponds to
your MySQL server version for the
right syntax to use near 'SELECT
count(*) as daily_count
FROM orders201003
SELECT CASE WHEN daily_count > 8000 THEN 0 WHEN daily_count > 6000 THEN 1 ELSE 2 END
FROM (
SELECT count(*) as daily_count
FROM orders201003
WHERE DATE_FORMAT(date_sub(curdate(), INTERVAL 1 DAY),"%d-%m-%y") =
DATE_FORMAT(reqDate,"%d-%m-%y")
) AS q
Also note that the nested queries are only supported starting from MySQL 4.1.