In my database, I have a table that contains all the versions of a certain translated strings. I want to create a view that returns a table with all the latest versions of each string.
I've come up with the following solution.
Table Strings
idString, Date, String, Chapter
These are the attributes of the table
View StringOrdered
Create View StringOrdered(idString, Date, String, Chapter) AS
SELECT * FROM Strings ORDER BY Date DESC
This is the supporting view.
View LastVersion
CREATE VIEW LastVersion (idString, Date, String, Chapter) AS
SELECT * FROM StringOrdered GROUP BY Chapter
Is there a way to obtain it without the supporting view?
You are depending on the group by taking the last version of something, based on the ordering of the data. Although this might work in practice, MySQL documentation specifically says this is not supported. Instead, try something like:
create view LastVersion as
select s.*
from strings s
where s.chapter = (select max(chapter)
from strings s2
where s.String = s2.String
)
I am assuming that you are looking for the latest chapter for String. If you are looking for the latest chapter for StringId, then change the where statement accordingly.
Note that MySQL does not support subqueries in the from clause of a view. The quote is quite clear (here):
The SELECT statement cannot contain a subquery in the FROM clause.
It does support subqueries in other clauses.
Related
I have a doubt and question regarding alias in sql. If i want to use the alias in same query can i use it. For eg:
Consider Table name xyz with column a and b
select (a/b) as temp , temp/5 from xyz
Is this possible in some way ?
You are talking about giving an identifier to an expression in a query and then reusing that identifier in other parts of the query?
That is not possible in Microsoft SQL Server which nearly all of my SQL experience is limited to. But you can however do the following.
SELECT temp, temp / 5
FROM (
SELECT (a/b) AS temp
FROM xyz
) AS T1
Obviously that example isn't particularly useful, but if you were using the expression in several places it may be more useful. It can come in handy when the expressions are long and you want to group on them too because the GROUP BY clause requires you to re-state the expression.
In MSSQL you also have the option of creating computed columns which are specified in the table schema and not in the query.
You can use Oracle with statement too. There are similar statements available in other DBs too. Here is the one we use for Oracle.
with t
as (select a/b as temp
from xyz)
select temp, temp/5
from t
/
This has a performance advantage, particularly if you have a complex queries involving several nested queries, because the WITH statement is evaluated only once and used in subsequent statements.
Not possible in the same SELECT clause, assuming your SQL product is compliant with entry level Standard SQL-92.
Expressions (and their correlation names) in the SELECT clause come into existence 'all at once'; there is no left-to-right evaluation that you seem to hope for.
As per #Josh Einstein's answer here, you can use a derived table as a workaround (hopefully using a more meaningful name than 'temp' and providing one for the temp/5 expression -- have in mind the person who will inherit your code).
Note that code you posted would work on the MS Access Database Engine (and would assign a meaningless correlation name such as Expr1 to your second expression) but then again it is not a real SQL product.
Its possible I guess:
SELECT (A/B) as temp, (temp/5)
FROM xyz,
(SELECT numerator_field as A, Denominator_field as B FROM xyz),
(SELECT (numerator_field/denominator_field) as temp FROM xyz);
This is now available in Amazon Redshift
E.g.
select clicks / impressions as probability, round(100 * probability, 1) as percentage from raw_data;
Ref:
https://aws.amazon.com/about-aws/whats-new/2018/08/amazon-redshift-announces-support-for-lateral-column-alias-reference/
You might find W3Schools "SQL Alias" to be of good help.
Here is an example from their tutorial:
SELECT po.OrderID, p.LastName, p.FirstName
FROM Persons AS p,
Product_Orders AS po
WHERE p.LastName='Hansen' AND p.FirstName='Ola'
Regarding using the Alias further in the query, depending on the database you are using it might be possible.
mysql table has columns order-id, order-item-id. Need to create a expression for generated column (new feature on 5.7) please have a look at the image for v virtual generated column look at the image for easy understanding. Tablename is daily-orders
Due to limitations on generated columns, you cannot create an expression that contains subqueries:
Subqueries, parameters, variables, stored functions, and user-defined functions are not permitted.
Since you would like to obtain information on data in multiple records, you have to use a subquery and that is not permitted. However, you can create a view instead of a generated column that contains this additional data. The following sql statement could form the core of such view:
select d1.*, d2.NumberOfItems
from dailyorders d1
inner join (select orderid, count(*) as NumberOfItems from dailyorders group by orderid) d2
on d1.orderid=d2.orderid
While editing some queries to add alternatives for columns without values, I accidentally wrote something like this (here is the simplyfied version):
SELECT id, (SELECT name) FROM t
To my surprise, MySQL didn't throw any error, but completed the query giving my expected results (the name column values).
I tried to find any documentation about it, but with no success.
Is this SQL standard or a MySQL specialty?
Can I be sure that the result of this syntax is really the column value from the same (outer) table? The extended version would be like this:
SELECT id, (SELECT name FROM t AS t1 where t1.id=t2.id) FROM t AS t2
but the EXPLAIN reports No tables used in the Extra column for the former version, which I think is very nice.
Here's a simple fiddle on SqlFiddle (it keeps timing out for me, I hope you have better luck).
Clarification: I know about subqueries, but I always wrote subqueries (correlated or not) that implied a table to select from, hence causing an additional step in the execution plan; my question is about this syntax and the result it gives, that in MySQL seems to return the expected value without any.
What you within your first query is a correlated subquery which simply returns the name column from the table t. no actual subquery needs to run here (which is what your EXPLAIN is telling you).
In a SQL database query, a correlated subquery (also known as a
synchronized subquery) is a subquery (a query nested inside another
query) that uses values from the outer query.
https://en.wikipedia.org/wiki/Correlated_subquery
SELECT id, (SELECT name) FROM t
is the same as
SELECT id, (SELECT t.name) FROM t
Your 2nd query
SELECT id, (SELECT name FROM t AS t1 where t1.id=t2.id) FROM t AS t2
Also contains correlated subquery but this one is actually running a query on table t to find records where t1.id = t2.id.
This is the default behavior for the SQL language and it is defined on the SQL ANSI 2011 over ISO/IEC 9075-1:2011(en) documentation. Unfortunately it is not open. This behavior is described on the section 4.11 SQL-Statements.
This behavior happens because the databases process the select comand without the from clause, therefore if it encounters:
select id, (select name) from some
It will try to find that name field as a column of the outer queries to process.
Fortunately I remember that some while ago I've answered someone here and find a valid available link to an SQL ANSI document that is online in FULL but it is for the SQL ANSI 99 and the section may not be the same one as the new document. I think, did not check, that it is around the section 4.30. Take a look. And I really recommend the reading (I did that back in the day).
Database Language SQL - ISO/IEC 9075-2:1999 (E)
It's not standard. In oracle,
select 1, (select 2)
from dual
Throws error, ORA-00923: FROM keyword not found where expected
How can you be sure of your results? Get a better understanding of what the query is supposed to acheive before you write it. Even the exetended version in the question does not make any sense.
I have tables: documents, languages and document_languages. Documents exist in one or more languages and this relationship is mapped in document_languages.
Imagine now I want to display the documents and all of its languages on a page, and paginate my result set to show 10 records on each page. There will be a WHERE statement, specifying which languages should be retrieved (ex: en, fr, it).
Even though I only want to display 10 documents on the page (LIMIT 10), I have to return more than 10 records if a document has more than one language (which most do).
How can you combine the WHERE statement with the LIMIT in a single query to get the records I need?
Use sub query to filter only documents records
select * from
(select * from documents limit 0,10) as doc,
languages lan,
document_languages dl
where doc.docid = dl.docid
and lan.langid = dl.langid
Check sub query doc as well
http://dev.mysql.com/doc/refman/5.0/en/from-clause-subqueries.html
http://dev.mysql.com/doc/refman/5.0/en/subqueries.html
You can add a little counter to each row counting how many unique documents you're returning and then return just 10. You just specify what document_id to start with and then it returns the next coming 10.
SELECT document_id,
if (#storedDocumentId <> document_id,(#docNum:=#docNum+1),#docNum),
#storedDocumentId:=document_id
FROM document, document_languages,(SELECT #docNum:=0) AS document_count
where #docNum<10
and document_id>=1234
and document.id=document_languages.document_id
order by document_id;
I created these tables:
create table documents (iddocument int, name varchar(30));
create table languages (idlang char(2), lang_name varchar(30));
create table document_languages (iddocument int, idlang char(2));
Make a basic query using GROUP_CONCAT function to obtain the traspose of languages results:
select d.iddocument, group_concat(dl.idlang)
from documents d, document_languages dl
where d.iddocument = dl.iddocument
group by d.iddocument;
And finally set the number of the documents with LIMIT option:
select d.iddocument, group_concat(dl.idlang)
from documents d, document_languages dl
where d.iddocument = dl.iddocument
group by d.iddocument limit 10;
You can check more info about GROUP_CONCAT here: http://dev.mysql.com/doc/refman/5.0/es/group-by-functions.html
Hmmmm... so, if you post your query (SQL statement), it might be easier to spot the error. Your outermost LIMIT statement should "do the trick." As Rakesh said, you can use subqueries. However, depending on your data, you may (probably) just want to use simple JOINs (e.g. where a.id = b.id...).
This should be fairly straightforward in MySQL. In the unlikely case that you're doing something "fancy," you can always pull the datasets into variables to be parsed by an external language (e.g., Python). In the case that you're literally just trying to limit screen output (interactive session), check-out the "pager" command (I like "pager less;").
Lastly, check-out using the UNION statement. I hope that something, here, is useful. Good luck!
I have a table which is grouped by city,side column for a unique entry, and I need to query latest entries of each city,side group. (newer entry always have higher timestamp value)
In SQLite I can use GROUP BY for the job: http://sqlfiddle.com/#!5/6c1c4/1/0
but in MySQL, it doesn't work in this way: http://sqlfiddle.com/#!9/9ead9/1/0
I think I misuse/abuse GROUP BY here, but how can I have a correct statement for by MySQL and SQLite?
In your examples, both MySQL and SQLite are breaking SQL standard.
In standard SQL (supported by all major SQL engines), if you use GROUP BY in SELECT statement, then the only expressions permitted in SELECT list are either columns listed in GROUP BY, or aggregate function calls (like count(), sum(), avg(), etc) over any other columns.
Most SQL engines: PostgreSQL, Oracle, MSSQL, DB2 follow this rule strictly - they do not permit any other syntax.
Both MySQL and SQLite, however, decided to be more lax in that regard, which I think is big mistake and endless source for confusion. While it seems to work, it is absolutely not clear what is really happening. For example, SQLite timestamp column generated by your query does not look like anything that was present in original table source.
If you don't want to have any surprises, you should follow the standard. In your case, it means using statement like:
SELECT
min(period),
side,
city,
min(gold),
min(silver),
min(normal),
min(timestamp)
FROM cities
GROUP BY city, side
ORDER BY min(timestamp)
When you use it, both MySQL and SQLite (and any other database for that matter) return identical results: SQLFiddle for MySQL, SQLFiddle for SQLite.
UPDATE:
This statement will do what you want in standards-compliant SQL:
SELECT
c.period,
g.side,
g.city,
c.gold,
c.silver,
c.normal,
g.timestamp
FROM cities c,
(SELECT
side,
city,
max(timestamp) AS timestamp
FROM cities
GROUP BY city, side) g
WHERE c.side = g.side
AND c.city = g.city
AND c.timestamp = g.timestamp
ORDER BY c.timestamp
It generates identical result for both MySQL and SQLite.
(There is only one catch: it assumes that timestamp is unique per group).
In the absence of any aggregating functions (and I'm talking specifically about MySQL here), the use of a GROUP BY clause is inappropriate and meaningless. Perhaps you meant to use the DISTINCT operator.