I think you might be misunderstanding where aliased columns are available/able to be referenced by the aliased name, particularly because you said (paraphrase) "an alias I created on line 3 of the sql wasn't available on line 4":
Wrong:
SELECT
1200 as games_won,
25 as years_played,
--can't use these aliases below in the same select block that they were declared in
games_won / years_played as games_won_per_year
...
Right:
SELECT
1200 as games_won,
25 as years_played,
--can use the values though
1200 / 25 as games_won_per_year
Right:
SELECT
games_won / years_played as games_won_per_year --alias from inner scope is available in this outer scope
FROM
(
SELECT
--these aliases only become available outside the brackets
1200 as games_won,
25 as years_played
) x
You can't alias a column and use the alias again in the same select block; you can only alias in an inner/subquery and use the alias in an outer query. SQL is not like a programming language that operates line by line:
int gameswon = 1200;
int yearsplayed = 25;
int winsperyear = gameswon / yearsplayed;
Here in this C# you can see we declare variables (aliases) on earlier lines and use them on later lines but that's because the programming language operates line by line. The results of an earlier line execution are available to later lines. SQL doesn't work like that; SQL works on entire sections of the query at a time. Your columns don't acquire those aliases you gave them until the entire select block is finished being processed so you cannot give a column or calculation an alias and then use that alias again in the same select block. The only way to get round this and create an alias that you will later use repeatedly is to create the alias in a subquery.
Here's another example:
SELECT
fih.tot_amt / fih.amt_per_proj AS allocation_amount
FROM
(
SELECT
CAST(f.total_net_amount_amount AS DECIMAL(18,8)) as tot_mt,
CAST(SUM(f.total_net_amoun_amount) OVER (PARTITION BY p.project_key)) AS DECIMAL(18,8)) AS amt_per_proj
FROM
dbo.factinvoiceheader f
INNER JOIN
dbo.dimproject p
ON ...
) fih
Here you can see I pulled the columns I wanted and aliased them in an inner query and then used the aliases in the outer query - it works because the aliases decalred inside the inner block are made available to the outer block
Always remember that SQL is not line by line line a typical programming language, but block by block. Indeed in most programming languages, things declared in inner code blocks are not available in outer code blocks (unless they're some globalised thing like javascript var) so SQL is a departure from what you're used to. Every time you create a block of instructions in SQL you have an opportunity to re-alias the columns of data.
Because SQL is block by block based, I indent my SQLs in blocks to make it easy to see what gets processed together. Keywords like SELECT, FROM, WHERE, GROUP BY and ORDER BY denote blocks and aliases can be created for columns in a SELECT, and for tables in a FROM. In taking your example above I've applied aliases not just to the calculations and columns but to the tables as well. It makes the query massively easier to read when it's indented and aliased throughout- give your table names an alias rather than writing dbo.factinvoiceheader.
before every column name
Here's a set of tips for making your SQLs neater and easier to read and debug:
- don't put them all on one line or at the same indent level - indent according to how deep or shallow the block of instructions is
- select, from, where, group by, order by etc denote the start of a block of operations - indent them all to the same level and indent their sub-instructions another level (if your select is indent level 2, the columns being selected should be indent level 3)
- when you have an inner query indent that too unless it's really simple and reads nicely as a one liner
- use lowercase for column and table names, upper case for reserved words, functions, datatypes (some people prefer camel case for functions)
- decide whether to use canelCase or underscore_style to split your words and keep to it
- always alias tables, and always select columns as tablealias.columnname - this prevents your query breaking in future if a table has a column added that is the same name as an original column you selected without qualifying what table the original column came from
- aliasing tables allows another vital operation; repeatedly joining the same table into a query. If your Person table has a WorkAddress and a HomeAddress the only way you can join the address table in twice to get both addresses for a person, is to alias the table (
person join address h on p.homeaddressid = h.id join address w on p.workaddressid = w.id
)