How to use ORDER BY in subqueries - sql

update dbo.Sheet1$ set F01 = 0 where ID in(
select top 3 ID from dbo.Sheet1$ where ID in(
select ID, ISNULL(F01,0) + ISNULL(F02,0) + ISNULL(F03,0) as RowSum
from dbo.Sheet1$ where F01 = 1 AND F02 = 1 order by RowSum desc))
Running this code I get the error message:
Msg 1033, Level 15, State 1, Line 1 The ORDER BY clause is invalid in
views, inline functions, derived tables, subqueries, and common table
expressions, unless TOP, OFFSET or FOR XML is also specified.
can I write another query that exactly does the same like above query?

Your query has more issues than just ordering without sampling. For example, when you use a subquery inside the IN () predicate, it cannot return more than 1 column, while your returns two.
Check this one, maybe I have guessed it correctly:
update dbo.Sheet1$ set F01 = 0
where ID in (
select top 3 ID
from dbo.Sheet1$
where F01 = 1 AND F02 = 1
order by ISNULL(F01,0) + ISNULL(F02,0) + ISNULL(F03,0) desc
);

2 possible reasons for the errors are:
1) You subquery has condition IN which tries to look for 2 column in the subquery while you can have only 1 column while you query IN
2) Your subquery can not use Order By because whether you're going to sort or not, it'll look for matching records from the subquery result set and update the records. So there won't be any meaning of using Order By in your subquery. Whether you'll keep Order By or remove Order By in your subquery, it'll give you the same output.
Hope this helps!

Related

I want to find the longest varchar in a specific column of a SQL Server table

Here's an example:
ID = INT IDENTITY
DESC = VARCHAR(5000)
ID | Desc
---|-----
1 | a
2 | aaa
3 | aa
The SQL query here should return 3 and the word itself i.e., aaa? Since the longest value is aaa with 3 characters?
The output should be:
aaa 3
You can use order by and limit the results to one row:
select description, len(description)
from t
order by len(description) desc
offset 0 row fetch first 1 row only;
Or use:
select top (1) description, len(description)
from t
order by len(description) desc;
With OFFSET & FETCH:
OFFSET is specifying how many rows from the top of the result set you will ignore ...
And then the FETCH is used to limit the result to the number of rows you specify.
The combination of this two will return the desired result because the result set is ordered by len(descr).
select descr, len(descr)
from test
order by len(descr) desc
OFFSET 0 ROWS
fetch first 1 rows only;
With TOP:
TOP will limit the reultset to the number of rows you specify. In this example 1. Because it is ordered by the lenght of the column desc then the first row will be the one you are looking for.
select top 1 descr, len(descr)
from test
order by len(descr) desc
HERE IS A DEMO for both examples
Note for the end:
Use aliases (I will demonstrate on one of this two examples)
select descr as Description, len(descr) "Description length"
from test
order by len(descr) desc
OFFSET 0 ROWS
fetch first 1 rows only;
You can put them between double quotes or not, use keyword as or not, it is up to you but the result looks better with them.
Both ROW and ROWS keyword is ok for OFFSET and FETCH clause. So what ever you want to use you can...
You can try TOP as shown below. Here is the official documentation of TOP (Transact-SQL).
Select
top 1 Id, description, len(description) as MaxLength
from YourTable order by len(description) desc
Live Demo
This will do what you want on Oracle (one of the tags on the question):
SELECT descript,length(descript)
FROM t WHERE length(descript)=( SELECT max(length(descript)) FROM t);
Keep in mind that if there are two (or more) records with that length, you will get them all. You could add a AND rownum < 2 clause but that will give you which ever entry the database chooses to give.
If you want the first record of those with the longest field, a subquery will be required:
SELECT descript,length(descript) from (
select descript from t order by length(descript) desc, descript asc)
WHERE rownum < 2;
There is also the offset 0 row fetch... version listed earlier:
SQL> select descript, length(descript)
2 from t
3 order by length(descript) desc
4 offset 0 row fetch first 1 row only;
DESCRIPT LENGTH(DESCRIPT)
-------------------- ----------------
defg 4
Mind you, this only works on my "12.2.0.1.0" Oracle. If I try the same thing on my "11.2.0.4.0" Oracle, I get "ORA-00933: SQL command not properly ended" with that last line. Apparently this is newer, maybe that is why I have never seen it before.

SQL: Most efficient way to select sequences of rows from a table

I have a tagged textual corpus stored in an SQL table like the following:
id tag1 tag2 token sentence_id
0 a e five 1
1 b f score 1
2 c g years 1
3 d h ago 1
My task is to search the table for sequences of tokens that meet certain criteria, sometimes with gaps between each token.
For example:
I want to be able to search for a sequence similar to the following:
the token has the value a in the tag1 column, and
the second token is one to two rows away from the first, and has the value g in tag2 or b in tag1, and
the third token should be at least three rows away, and has ago in the token column.
In SQL, this would be something like the following:
SELECT * FROM my_table t1
JOIN my_table t2 ON t1.sentence_id = t2.sentence_id
JOIN my_table t3 ON t3.sentence_id = t1.sentence_id
WHERE t1.tag1 = 'a' AND (t2.id = t1.id + 1 OR t2.id = t1.id + 2)
AND (t2.tag2 = 'g' OR t2.tag1 = 'b')
AND t3.id >= t1.id + 3 AND t3.token = 'ago'
So far I have only been able to achieve this by joining the table by itself each time I specify a new token in the sequence (e.g. JOIN my_table t4), but with millions of rows this gets quite slow. Is there a more efficient way to do this?
You could try this staged approach:
apply each condition (other than the various distance conditions) as a subquery
Calculate the distances between the tokens which meet the conditions
Apply all the distance conditions separately.
This might improve things, if you have indexes on the tag1, tag2 and token columns:
SELECT DISTINCT sentence_id FROM
(
-- 2. Here we calculate the distances
SELECT cond1.sentence_id,
(cond2.id - cond1.id) as cond2_distance,
(cond3.id - cond1.id) as cond3_distance
FROM
-- 1. These are all the non-distance conditions
(
SELECT * FROM my_table WHERE tag1 = 'a'
) cond1
INNER JOIN
(
SELECT * FROM my_table WHERE
(tag1 = 'b' OR tag2 = 'g')
) cond2
ON cond1.sentence_id = cond2.sentence_id
INNER JOIN
(
SELECT * FROM my_table WHERE token = 'ago'
) cond3
ON cond1.sentence_id = cond3.sentence_id
) conditions
-- 3. Now apply the distance conditions
WHERE cond2_distance BETWEEN 0 AND 2
AND cond3_distance >= 3
ORDER BY sentence_id;
If you apply this query to this SQL fiddle you get:
| sentence_id |
|-------------|
| 1 |
| 4 |
Which is what you want. Now whether it's any faster or not, only you (with your million-row database) can really tell, but from the perspective of having to actually write these queries, you'll find they're much easier to read, understand and maintain.
You need to edit your question and give more details on how these sequences of tokens work (for instance, what does "each time I specify a new token in the sequence" mean in practice?).
In postgresql you can solve this class of queries with a window function. Following your exact specification above:
SELECT *,
CASE
WHEN lead(tag2, 2) OVER w = 'g' THEN lead(token, 2) OVER w
WHEN lead(tag1) OVER w = 'b' THEN lead(token) OVER w
ELSE NULL::text
END AS next_token
FROM my_table
WHERE tag1 = 'a'
AND next_token IS NOT NULL
WINDOW w AS (PARTITION BY sentence_id ORDER BY id);
The lead() function looks ahead a number of rows (default is 1, when not specified) from the current row in the window frame, in this case all rows with the same sentence_id as specified in the partition of the window definition. So, lead(tag1, 2) looks at the value of tag1 two rows ahead to compare against your condition, and lead(token, 2) returns the token from two rows ahead as column next_token in the current row and having the same sentence_id. If the first CASE condition fails, the second is evaluated; if that fails NULL is returned. Note that the order of the conditions in the CASE clause is significant: different ordering gives different results.
Obviously, if you keep on adding conditions for subsequent tokens the query becomes very complex and you may have to put individual search conditions in separate stored procedures and then call these depending on your requirements.

How do I preserve the order of a SQL query using the IN command

SELECT * FROM tblItems
WHERE itemId IN (9,1,4)
Returns in the order that SQL finds them in (which happens to be 1, 4, 9) however, I want them returned in the order that I specified in the array.
I know I could reorder them after in my native language (obj c), but is there a neat way to do this in SQL?
Somthing like this would be great:
ORDER BY itemId (9,1,4) -- <-- this dosn't work :)
Probably the best way to do this is create a table of item IDs, which also includes a rank order. Then you can join and sort by the rank order.
Create a table like this:
itemID rank
9 1
1 2
4 3
Then your query would look like this:
select tblItems.* from tblItems
inner join items_to_get on
items_to_get.itemID = tblItems.itemID
order by rank
Use a CASE expression to map the ID values to an increasing sequence:
... ORDER BY CASE itemId
WHEN 9 THEN 1
WHEN 1 THEN 2
ELSE 3
END
I had the same task once in a mysql environment.
I ended up using
ORDER BY FIND_IN_SET(itemID, '9,1,4')
this is working for me since then. I hope it also works for sqlite
You can add a case construct to your select clause.
select case when itemid = 9 then 1
when itemid = 1 then 2 else 3 end sortfield
etc
order by sortfield
You could create a procedure to order the data in SQL, but that would be much more complicated than its native language counterpart.
There's no "neat way" to resort the data like that in SQL -- the WHERE clause of a SELECT simply says "if these criteria are matched, include the row"; it's not (and it cannot be) an ordering criterion.

How to group by a column

Hi I know how to use the group by clause for sql. I am not sure how to explain this so Ill draw some charts. Here is my original data:
Name Location
----------------------
user1 1
user1 9
user1 3
user2 1
user2 10
user3 97
Here is the output I need
Name Location
----------------------
user1 1
9
3
user2 1
10
user3 97
Is this even possible?
The normal method for this is to handle it in the presentation layer, not the database layer.
Reasons:
The Name field is a property of that data row
If you leave the Name out, how do you know what Location goes with which name?
You are implicitly relying on the order of the data, which in SQL is a very bad practice (since there is no inherent ordering to the returned data)
Any solution will need to involve a cursor or a loop, which is not what SQL is optimized for - it likes working in SETS not on individual rows
Hope this helps
SELECT A.FINAL_NAME, A.LOCATION
FROM (SELECT DISTINCT DECODE((LAG(YT.NAME, 1) OVER(ORDER BY YT.NAME)),
YT.NAME,
NULL,
YT.NAME) AS FINAL_NAME,
YT.NAME,
YT.LOCATION
FROM YOUR_TABLE_7 YT) A
As Jirka correctly pointed out, I was using the Outer select, distinct and raw Name unnecessarily. My mistake was that as I used DISTINCT , I got the resulted sorted like
1 1
2 user2 1
3 user3 97
4 user1 1
5 3
6 9
7 10
I wanted to avoid output like this.
Hence I added the raw id and outer select
However , removing the DISTINCT solves the problem.
Hence only this much is enough
SELECT DECODE((LAG(YT.NAME, 1) OVER(ORDER BY YT.NAME)),
YT.NAME,
NULL,
YT.NAME) AS FINAL_NAME,
YT.LOCATION
FROM SO_BUFFER_TABLE_7 YT
Thanks Jirka
If you're using straight SQL*Plus to make your report (don't laugh, you can do some pretty cool stuff with it), you can do this with the BREAK command:
SQL> break on name
SQL> WITH q AS (
SELECT 'user1' NAME, 1 LOCATION FROM dual
UNION ALL
SELECT 'user1', 9 FROM dual
UNION ALL
SELECT 'user1', 3 FROM dual
UNION ALL
SELECT 'user2', 1 FROM dual
UNION ALL
SELECT 'user2', 10 FROM dual
UNION ALL
SELECT 'user3', 97 FROM dual
)
SELECT NAME,LOCATION
FROM q
ORDER BY name;
NAME LOCATION
----- ----------
user1 1
9
3
user2 1
10
user3 97
6 rows selected.
SQL>
I cannot but agree with the other commenters that this kind of problem does not look like it should ever be solved using SQL, but let us face it anyway.
SELECT
CASE main.name WHERE preceding_id IS NULL THEN main.name ELSE null END,
main.location
FROM mytable main LEFT JOIN mytable preceding
ON main.name = preceding.name AND MIN(preceding.id) < main.id
GROUP BY main.id, main.name, main.location, preceding.name
ORDER BY main.id
The GROUP BY clause is not responsible for the grouping job, at least not directly. In the first approximation, an outer join to the same table (LEFT JOIN below) can be used to determine on which row a particular value occurs for the first time. This is what we are after. This assumes that there are some unique id values that make it possible to arbitrarily order all the records. (The ORDER BY clause does NOT do this; it orders the output, not the input of the whole computation, but it is still necessary to make sure that the output is presented correctly, because the remaining SQL does not imply any particular order of processing.)
As you can see, there is still a GROUP BY clause in the SQL, but with a perhaps unexpected purpose. Its job is to "undo" a side effect of the LEFT JOIN, which is duplication of all main records that have many "preceding" ( = successfully joined) records.
This is quite normal with GROUP BY. The typical effect of a GROUP BY clause is a reduction of the number of records; and impossibility to query or test columns NOT listed in the GROUP BY clause, except through aggregate functions like COUNT, MIN, MAX, or SUM. This is because these columns really represent "groups of values" due to the GROUP BY, not just specific values.
If you are using SQL*Plus, use the BREAK function. In this case, break on NAME.
If you are using another reporting tool, you may be able to compare the "name" field to the previous record and suppress printing when they are equal.
If you use GROUP BY, output rows are sorted according to the GROUP BY columns as if you had an ORDER BY for the same columns. To avoid the overhead of sorting that GROUP BY produces, add ORDER BY NULL:
SELECT a, COUNT(b) FROM test_table GROUP BY a ORDER BY NULL;
Relying on implicit GROUP BY sorting in MySQL 5.6 is deprecated. To achieve a specific sort order of grouped results, it is preferable to use an explicit ORDER BY clause. GROUP BY sorting is a MySQL extension that may change in a future release; for example, to make it possible for the optimizer to order groupings in whatever manner it deems most efficient and to avoid the sorting overhead.
For full information - http://academy.comingweek.com/sql-groupby-clause/
SQL GROUP BY STATEMENT
SQL GROUP BY clause is used in collaboration with the SELECT statement to arrange identical data into groups.
Syntax:
1. SELECT column_nm, aggregate_function(column_nm) FROM table_nm WHERE column_nm operator value GROUP BY column_nm;
Example :
To understand the GROUP BY clauserefer the sample database.Below table showing fields from “order” table:
1. |EMPORD_ID|employee1ID|customerID|shippers_ID|
Below table showing fields from “shipper” table:
1. | shippers_ID| shippers_Name |
Below table showing fields from “table_emp1” table:
1. | employee1ID| first1_nm | last1_nm |
Example :
To find the number of orders sent by each shipper.
1. SELECT shipper.shippers_Name, COUNT (orders.EMPORD_ID) AS No_of_orders FROM orders LEFT JOIN shipper ON orders.shippers_ID = shipper.shippers_ID GROUP BY shippers_Name;
1. | shippers_Name | No_of_orders |
Example :
To use GROUP BY statement on more than one column.
1. SELECT shipper.shippers_Name, table_emp1.last1_nm, COUNT (orders.EMPORD_ID) AS No_of_orders FROM ((orders INNER JOIN shipper ON orders.shippers_ID=shipper.shippers_ID) INNER JOIN table_emp1 ON orders.employee1ID = table_emp1.employee1ID)
2. GROUP BY shippers_Name,last1_nm;
| shippers_Name | last1_nm |No_of_orders |
for more clarification refer my link
http://academy.comingweek.com/sql-groupby-clause/

Subselect in ORDER BY? Valid SQL?

Is this valid SQL? If yes, could you please tell me what it does?
Select *
from MyFirstTable
order by (select min(somefield)
from MySecondTable
where MyFirstTable.id = MySecondTable.id)
A subselect in an "order by", how is that possible?? In effect this SQL query does not sort by a field, but by some value in a row of a field (min). It does not seem logical so sort by anything else other than a field name. But min(somefield) <> somefield! But, yes, this query works and someone at work who teaches me told me this, and i'm sceptical.
Can you tell me what this means? Or just post an equivilant query?
Thanks!
This query orders MyFirstTable by the minimum value of somefield stored in MySecondTable under the same id.
Here's a quick example:
MyFirstTable
id
1
2
3
MySecondTable
id somefield
1 2
1 4
2 1
3 6
3 4
In the above case, your query would return
id
2
1
3
An equivalent query that may make more sense:
SELECT MyFirstTable.ID, MyFirstTable.A, MyFirstTable.B
FROM MyFirstTable
INNER JOIN MySecondTable ON MyFirstTable.ID = MySecondTable.ID
GROUP BY MyFirstTable.ID, MyFirstTable.A, MyFirstTable.B
ORDER BY MIN(MySecondTable.SomeField)