Can I add logic to SQL UNION? - sql

I'm not an expert at SQL at all, so I want to know is there a way to execute SQL queries with some logic without any additional functions or stored procedures. I have two tables: governor and figure. I need to union them by the column full_name and add additional column where it will say is a person a governor or no. But I have no clue how to do it. This is my initial query:
SELECT full_name FROM governor
UNION
SELECT full_name FROM figure
Is there a way to do this only within the query?

Yes, people do this with unions all the time. Just select a constant and give it an appropriate alias (in the first select; aliases/column names in later selects are ignored):
SELECT full_name, 'governor' AS governor_or_figure FROM governor
UNION ALL
SELECT full_name, 'figure' FROM figure
Just "UNION" defaults to UNION DISTINCT, which is only what is actually intended a minority of the time. Specifying "UNION ALL" usually what you want.

Related

Oracle SQL: single SELECT request for multiple owners

I would like your advice on the best method to use.
From a single Oracle server, we have 3 different owners that contain the exact same tables/data structures. Essentially, the owners allow us to separate the data by administrative regions.
Currently, when I want to do a SQL query on the entire data set (regions), I have to do 3 separate queries:
select * from owner1.Table1 union all
select * from owner2.Table1 union all
select * from owner3.Table1
In this simple example, there are no issues, but if the query is complex, it quickly becomes very difficult to maintain.
So, would there be a more efficient way to make only one global query instead of 3. I guess it's possible to do it via a PL/SQL script, or Dynamic SQL, but I don't know...
Basically, I would like to be able to do (where owners would contain the names of my 3 owners):
select * from owners.Table1
It is not possible to build views that would contain the data of all 3 owners (there would be too many).
Thanks
In this simple example, there are no issues, but if the query is complex, it quickly becomes very difficult to maintain.
So, would there be a more efficient way to make only one global query instead of 3.
Use a sub-query factoring clause (a.k.a. a CTE) to combine the queries with the simple UNION ALL queries and then perform the complex query on the combined table (rather than trying to perform the queries on the individual tables):
WITH subquery_name AS (
select * from owner1.Table1 union all
select * from owner2.Table1 union all
select * from owner3.Table1
)
SELECT <your complex query>
FROM subquery_name;

When does the aliasing take effect? [duplicate]

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.

what is the corresponding query in Oracle db

I have the following query which works perfectly in postgresql:
Select 'Tom' as name
output as:
name
Tom
What should be the corresponding query in Oracle?
If I run the query in Oracle it gives an error, but is run successfully in postgresql.
Oracle doesn't allow queries without a from clause. For these kind of queries, Oracle provides a system table called dual, with one column and one row:
SELECT 'Tom' as name FROM dual
Oracle needs the from clause. In that case, you have to use the DUAL table.
select 'Tom' as name from dual;
Oracle is (afaik) unusual amongst other RMDBSs in that it enforces that if you're selecting something, you must select it from a table.
To that end, there is the DUAL table. It is a special, one row, one column table that allows you to select constants, functions etc within SQL, rather than having to write a PL/SQL procedure.
I say "special" because, since 10g, the optimizer recognises that it's different to other tables and can therefore make use of that information when generating the execution path to make it more efficient than if it was using a "normal" one column, one row table.

SQL querying multiple schemas

I am looking to run a query on several schemas in workbench. bascially, they are all symmetric , just different dates. In workbench, i can only select one of them and run the query. Is there a way to aggregate them and run the query over a selection of schemas?
EDIT:
To elaborate a bit more, I have schemas with names yyyy_mm_dd for each day. Ideally, instead of doing a union over them as suggested by Guish below, If would like a dynamic query that would be able to turn the name of the schema into a valid date and Union all of them where the date is within a defined range. Is this possible? I am using Oracle and sql workbench
I guess you are using mySql workbench.
Use an union operator.
(SELECT a FROM `schema1`.`t1` )
UNION
(SELECT a FROM `schema2`.`t1`);
Info here
You can then create a view from your query.
A thread here on querying multiple shema
In know Transact-SQL a lot more and it is similar.
SELECT ProductModelID, Name
FROM Schema1.ProductModel
UNION ALL
SELECT ProductModelID, Name
FROM Schema2.ProductModel
ORDER BY Name;

SQL multiple columns in IN clause

If we need to query a table based on some set of values for a given column, we can simply use the IN clause.
But if query need to be performed based on multiple columns, we could not use IN clause(grepped in SO threads.)
From other SO threads, we can circumvent this problem using joins or exists clause etc. But they all work if both main table and search data are in the database.
E.g
User table:
firstName, lastName, City
Given a list of (firstname, lastName) tuples, I need to get the cities.
I can think of following solutions.
1
Construct a select query like,
SELECT city from user where (firstName=x and lastName=y) or (firstName=a and lastName=b) or .....
2
Upload all firstName, lastName values into a staging table and perform a join between 'user' table and the new staging table.
Are there any options for solving this problem and what is the preferred of solving this problem in general?
You could do like this:
SELECT city FROM user WHERE (firstName, lastName) IN (('a', 'b'), ('c', 'd'));
The sqlfiddle.
It often ends up being easier to load your data into the database, even if it is only to run a quick query. Hard-coded data seems quick to enter, but it quickly becomes a pain if you start having to make changes.
However, if you want to code the names directly into your query, here is a cleaner way to do it:
with names (fname,lname) as (
values
('John','Smith'),
('Mary','Jones')
)
select city from user
inner join names on
fname=firstName and
lname=lastName;
The advantage of this is that it separates your data out of the query somewhat.
(This is DB2 syntax; it may need a bit of tweaking on your system).
In Oracle you can do this:
SELECT * FROM table1 WHERE (col_a,col_b) IN (SELECT col_x,col_y FROM table2)
In general you can easily write the Where-Condition like this:
select * from tab1
where (col1, col2) in (select col1, col2 from tab2)
Note
Oracle ignores rows where one or more of the selected columns is NULL. In these cases you probably want to make use of the NVL-Funktion to map NULL to a special value (that should not be in the values):
select * from tab1
where (col1, NVL(col2, '---') in (select col1, NVL(col2, '---') from tab2)
oracle sql
Ensure you have an index on your firstname and lastname columns and go with 1. This really won't have much of a performance impact at all.
EDIT: After #Dems comment regarding spamming the plan cache ,a better solution might be to create a computed column on the existing table (or a separate view) which contained a concatenated Firstname + Lastname value, thus allowing you to execute a query such as
SELECT City
FROM User
WHERE Fullname in (#fullnames)
where #fullnames looks a bit like "'JonDoe', 'JaneDoe'" etc
Determine whether the list of names is different with each query or reused. If it is reused, it belongs to the database.
Even if it is unique with each query, it may be useful to load it to a temporary table (#table syntax) for performance reasons - in that case you will be able to avoid recompilation of a complex query.
If the maximum number of names is fixed, you should use a parametrized query.
However, if none of the above cases applies, I would go with inlining the names in the query as in your approach #1.