SQL With clause, same column name in different tables - sql

I have two different tables. mode_table and station_table. They both have a column called day_column. I want to have the records from each table which day_column is greater than 20.
I am not sure if I am allowed to use the same alias (day_Val) in both tables. Is this correct?
WITH
active_mode AS (
SELECT (IF(day_column > '20', 0, 1)) AS day_Val
FROM mode_table
),
active_station AS (
SELECT (IF(day_column > '20', 0, 1)) AS day_Val
FROM station_table
)
SELECT day_column, day_val
FROM active_mode, active_station
WHERE day_Val != 1
The reason for this question is I have around 14 tables and I do not want to create 14 alias and use just one.
The goal is to report any row in all those 14 tables that has day_column > 20. All the tables have a column named day_column.

Do you just want union all:
select . . . -- whatever columns you want
from mode_table m
where day_column > 20
union all
select . . .
from station_table s
where day_column > 20

The immediate problem I can find from your script is there is no column called “day_column” from both “active_mode” and active_station.
Regarding to your question, the answer is yes, but not the way you are doing, you may consider to use a union all statement
For example
Select
Date_val
From active_mode
Union all
Select
Date_val
From active_station
Otherwise, it will cause ambiguity to the server as it don’t know which table expression are you referring to.

Related

SQL Server 'AS' alias unexpected syntax

I've come across following T-SQL today:
select c from (select 1 union all select 1) as d(c)
that yields following result:
c
-----------
1
1
The part that got me confused was d(c)
While trying to understand what's going on I've modified T-SQL into:
select c, b from (select 1, 2 union all select 3, 4) m(c, b)
which yields following result:
c b
----------- -----------
1 2
3 4
It was clear that d & m are table reference while letters in brackets c & b are reference to columns.
I wasn't able to find relevant documentation on msdn, but curious if
You're aware of such syntax?
What would be useful use case scenario?
select c from (select 1 union all select 1) as d(c)
is the same as
select c from (select 1 as c union all select 1) as d
In the first query you did not name the column(s) in your subquery, but named them outside the subquery,
In the second query you name the column(s) inside the subquery
If you try it like this (without naming the column(s) in the subquery)
select c from (select 1 union all select 1) as d
You will get following error
No column name was specified for column 1 of 'd'
This is also in the Documentation
As for the usage, some like to write it the first method, some in the second, whatever you prefer. It's all the same
An observation: Using the table constructor values gives you no way of naming the columns, which makes it neccessary to use column naming after the table alias:
select * from
(values
(1,2) -- can't give a column name here
,(3,4)
) as tableName(column1,column2) -- gotta do it here
You've already had comments that point you to the documentation of how derived tables work, but not to answer you question regarding useful use cases for this functionality.
Personally I find this functionality to be useful whenever I want to create a set of addressable values that will be used extensively in your statement, or when I want to duplicate rows for whatever reason.
An example of addressable values would be a much more compelx version of the following, in which the calculated values in the v derived table can be used many times over via more sensible names, rather than repeated calculations that will be hard to follow:
select p.ProductName
,p.PackPricePlusVAT - v.PackCost as GrossRevenue
,etc
from dbo.Products as p
cross apply(values(p.UnitsPerPack * p.UnitCost
,p.UnitPrice * p.UnitsPerPack * 1.2
,etc
)
) as v(PackCost
,PackPricePlusVAT
,etc
)
and an example of being able to duplicate rows could be in creating an exception report for use in validating data, which will output one row for every DataError condition that the dbo.Product row satisfies:
select p.ProductName
,e.DataError
from dbo.Products as p
cross apply(values('Missing Units Per Pack'
,case when p.SoldInPacks = 1 and isnull(p.UnitsPerPack,0) < 1 then 1 end
)
,('Unusual Price'
,case when p.Price > (p.UnitsPerPack * p.UnitCost) * 2 then 1 end
)
,(etc)
) as e(DataError
,ErrorFlag
)
where e.ErrorFlag = 1
If you can understand what these two scripts are doing, you should find numerous examples of where being able to generate additional values or additional rows of data would be very helpful.

How can I convert 2 row into column in tsql?

I have 2 row data which I want to make it to be 2 column,
I tried union syntax but it didn't work.
Here is the data I have:
breed 1 breed2
I tried to convert it with this sql
select a.breed union a.breed
but it didn't work.
Here is what you want from the SQL:
breed1,breed2
SELECT
[breed1],
[breed2]]
FROM
(
SELECT 'breed1' myColumn
union
select 'breed2'
) AS SourceTable
PIVOT
(
AVG(mySecondColumn) FOR
myColumn IN ([breed1], [breed2]])
) AS PivotTable;
You can use a self join. This needs a way to pair rows together (so if you have four rows you get 1 and 2 in one result and 3 and 4 in the other rather than another combination).
I'm going to assume you have sequentially numbered rows in an Id column and an odd numbered row is paired with the one greater even Id:
select odd.Data as 'First', even.Data as 'Second'
from TheData odd
inner join TheData even on odd.Id+1 = even.Id
where odd.Id % 2 = 1;
More generally for more columns use of pivot is more flexible.
How about an aggregation query?
select min(breed) as breed1, max(breed) as breed2
from t;

How to categorize several columns in one statement in SQL/PLSQL

I've got a table with 20 columns which I like to categorize like;
0-25 --> 1
25-50 --> 2
50-75 --> 3
75-100 --> 4
I prefer not to use 20 case ... when statements. Anyone who knows how to do this more dynamically & efficiently? Can be SQL or PL/SQL.
I tried some PL/SQL, but I didn't see a simple method to use the column names as variables.
Many thanks.
Frans
Your example is a bit confusing, but assuming you want to put a certain value into those categories, the function width_bucket might be what you are after:
Something like this:
with sample_data as (
select trunc(dbms_random.value(1,100)) as val
from dual
connect by level < 10
)
select val, width_bucket(val, 0, 100, 4) as category
from sample_data;
This will assign the numbers 1-4 to the (random) values from sample_data. the 0, 100 defines the range from which to build the buckets, and the final parameter 4 says in how many (equally wide) buckets this should be distributed. The result of the function is the bucket into which the value val would fall.
SQLFiddle example: http://sqlfiddle.com/#!4/d41d8/10721
The case statement is probably the most efficient way of doing it. A more dynamic way would be to create a table using the with statement. Here is an example of the code:
with ref as (
select 0 as lower, 25 as higher 1 as val from dual union all
select 25, 59, 2 from dual union all
select 50, 75, 3 from dual union all
select 75, 100, 4 from dual
)
select ref.val
from t left outer join ref
on t.col >= ref.lower and t.col < ref.higher
That said, this particular lookup could be done with arithmetic:
select trunc((t.col - 1) / 25) + 1 as val
from t
And, if your problem is managing the different columns, you might consider unpivot. However, I think it is probably easier just to write the code and modify the column names in a text editor or Excel.

Calculating a field from SQL Query Selecting from multiple tables with Union

I have the following query, which works great. The problem I have is that in both tables (and the aggregate unioned table), there is a field called MTGUID. I need to multiply MTGUID by a number (let's say 1.35, for ease of use) and have it return that number in the MTGUID field. I have tried a dozen ways to do this and can't get anything to play ball. I can create a new column for each calculated price, like (BKRETAIL.MTGUID * 1.35) AS MTG1, but we've got tens of thousands of lines of code that specifically use MTGUID. Any ideas?
I'm using Firebird SQL.
SELECT * FROM (
SELECT BKRETAIL.* FROM BKRETAIL WHERE BKRETAIL.MKEY='SOMEKEY'
UNION SELECT BKWHOLESALE.* FROM BKWHOLESALE WHERE MKEY='SOMEKEY')
ORDER BY
case STATUS
WHEN 'RT' then 1
WHEN 'WH' then 2
WHEN 'OL' then 3
WHEN 'OD' then 4
WHEN NULL then 5
else 6
end;
How about this:
SELECT MTGUID * 1.35 as calculatedMTGUID, SUBSEL.* FROM (
SELECT BKRETAIL.* FROM BKRETAIL WHERE BKRETAIL.MKEY='SOMEKEY'
UNION SELECT BKWHOLESALE.* FROM BKWHOLESALE WHERE MKEY='SOMEKEY') SUBSEL
ORDER BY
case STATUS
WHEN 'RT' then 1
WHEN 'WH' then 2
WHEN 'OL' then 3
WHEN 'OD' then 4
WHEN NULL then 5
else 6
end;
try this
SELECT MTGUID * 1.35 AS MTGUID,<list rest OF COLUMNS here>
FROM (
SELECT BKRETAIL.* FROM BKRETAIL WHERE BKRETAIL.MKEY='SOMEKEY'
UNION SELECT BKWHOLESALE.* FROM BKWHOLESALE WHERE MKEY='SOMEKEY')
ORDER BY
case STATUS
WHEN 'RT' then 1
WHEN 'WH' then 2
WHEN 'OL' then 3
WHEN 'OD' then 4
WHEN NULL then 5
else 6
end;
One option would be to replace the original MTGUID column with computed one, ie
rename the original MTGUID column in table(s);
add new MTGUID column with desired expression using COMPUTED BY (expr);
Advantage of this is that you don't have to alter your SQL statements, disadvantage is that you have to maintain the expression in many places (all the tables which have the column). Of course, the queries which need the original MTGUID value must be updated to use the renamed column, but if the number of such statements is significantly lower it could be worth the trouble.
I think a better solution would be to "hide" all this stuff behind a view but this requires alerting your SQL queries...

How do you select using a range of strings in SQL?

I have a table of vehicles with registration numbers, and want to select a subset of them that are between some user-supplied 'from' and 'to' values.
So lets say the table looks like this:
id reg_num
1 DD1111
2 DD1112
3 DE2245
4 EE5678
5 EF6547
The SQL I have so far looks like this:
select *
from vehicles
where reg_num >= 'DD' -- this value is user supplied
and reg_num <= 'DE' -- and so is this one
Which should (by my thinking) return:
1 DD1111
2 DD1112
3 DE2245
But instead, only returns:
1 DD1111
2 DD1112
I imagine that SQL server sees 'DE2245' as greater than 'DE', and so excludes the row.
My question: How do I get SQL server to include all rows that start with 'DE'?
You have to add 'zzzz's at the end as many as necessary to match your column width definition.
select * from vehicles
where reg_num >= 'DD' and reg_num <= 'DE' + 'ZZZZZZZZZZZZ'
where reg_num >= #userValueFrom
and left(reg_num,char_length(#userValueTo) <= #userValueTo
but please note that this where does not utilize any index because of a function on the column in SARG.
If the format is guaranteed, you can simply do:
SELECT *
FROM vehicles
WHERE LEFT(reg_num, 2) BETWEEN 'DD' AND 'DE'
But again, this is supposedly not SARGable - which always baffles me, because surely an index on reg_num can be used...
DE2245 is not less than DE. To make it more clear, DE2245 is less than DE3