Microsoft SQL -- struggles with CROSS JOIN - sql-server

So i have to do a cartesian product (or CROSS JOIN) between two tables. One problem is that both tables have a column with the name 'itemname'. My current case looks as follows:
select *
into #cartesian_temp
from xsale CROSS JOIN xitem
delete from #cartesian_temp where deptname='books' and itemcolor='bamboo'
select * from #cartesian_temp
so the error I get is:
Column names in each table must be unique. Column name 'itemname' in table '#cartesian_temp' is specified more than once
Anyone that can help me with my problem?

you can add alias for the columns like below.
select XS.itemname as saleitemname , XI.itemname as saleitemname2
into #cartesian_temp
from xsale XS CROSS JOIN xitem XI

This is one of the reasons why seasoned SQL pro's will ALWAYS advocate to give Tables an alias and to ALWAYS fully qualify every column name by using the alias. It's not just a cross join problem
Avoid this:
SELECT *
FROM
person
INNER JOIN
address
ON addressid = address.id -- Person.addressid
Sure, it'll work as long as the column names are all unique (it'll probably cause issues even now because person will have an I'd column and so will address) but it might stop working at any point in future if someone adds columns to either table with names that clash
Prefer this:
SELECT p.id as personid, a.id as addressid, p.name, a.zipcode
FROM
person p
INNER JOIN
address a
ON p.addressid = a.id
This is fully aliased (both tables have an alias) and we haven't used select *; weve fully qualified every column with a prefix using the table alias and we've aliased columns that have the same named (the ID columns) in each table so we can tel them apart. No one can add any columns to the db and cause this query to stop working
Aliasing Tables helps in another way; it lets us use the same table twice in a query. Suppose a person had a work address and a home address:
SELECT ...
FROM
person p
INNER JOIN
address awork
ON p.workaddressid = awork.id
INNER JOIN
address ahome
ON p.homeaddressid = ahome.id
This is impossible without aliasing. Always give aliases a sensible name (not a1, a2)
For your case, go like:
SELECT xs.itemname as xsitemname, xi.itemname as xiitemname, ...
FROM
xsale xs
CROSS JOIN
xitem xi
WHERE
xi.itemcolor = 'green'
This will be every green item crossed with every sale item

Related

SQL: Building hierarchies and nesting queries on the same table

I am trying to build hierarchies by nesting queries on the same table in MS SQL Server 2014.
To give an example of what I am trying to achieve:
I have a table 'employees' whith the following columns:
[ID],[First Name],[Last Name],[ReportsTo]
{1},{John},{Doe},{2}
{2},{Mary},{Miller},{NULL}
I am trying to build a statement, where I join the employees table with itself and where I build a hierarchy with the boss on top.
Expected Result:
[Employee],[Boss]
{Miller,Mary},{NULL}
{Doe, John},{Miller,Mary}
I apologize, if this is a stupid question, but I fail to create a working nested query.
Could you please help me with that?
Thank you very much in advance
Based on the intended results, it looks like what you essentially want is a list of employees. So let's start with that:
SELECT LastName, FirstName, ReportsTo FROM Employees
This gives you the list, so you now have the objects you're looking for. But you need to fill out more data. You want to follow ReportsTo and show data from the record to which that points as well. This would be done exactly as it would if the foreign key pointed to a different table. (The only difference from being the same table is that you must use table aliases in the query, since you're including the same table twice.)
So let's start by joining the table:
SELECT e.LastName, e.FirstName, e.ReportsTo
FROM Employees e
LEFT OUTER JOIN Employees b on e.ReportsTo = b.ID
The results should still be the same, but now you have more data to select from. So you can add the new columns to the SELECT clause:
SELECT
e.LastName AS EmployeeLastName,
e.FirstName AS EmployeeFirstName,
b.LastName AS BossLastName,
b.FirstName AS BossFirstName
FROM Employees e
LEFT OUTER JOIN Employees b on e.ReportsTo = b.ID
It's a join like any other, it just happens to be a join to the same table.

Query values from another table based on column value

I am attempting to create a query that pulls information from two other tables, however I only know which table to pull from based on a column in another table. I'm currently looking into doing this using a stored procedure (e.g. build the query and then run it) but I wanted to know if there is a better way to do this, or if I could accomplish it in a single query.
In terms of the connections, ID's are unique accross the entire database, so no two ID's will overlap. However I do not know which subtable the ID relates. I am able to find this by pulling in an unrelated table that happens to have the information (call it the Object Table). One of the columns will give me the table name for the information (in my example below, Person). I have drafted a simple example below. Can you see any way I could accomplish this in a single query? Something like this is what I am aiming for but I am starting to think its not possible.
SELECT * FROM base_table
LEFT JOIN object ON object.id = base_table.role
LEFT JOIN [object.type] tmp ON tmp.entity_id = base_table.entity_id
id | role | entity_id (Base Table)
---------------------
1 | 101 | 1000
id | type (Objects Table)
------------
101| person
entity_id | name | etc.. (Person Table)
------------------------
1000 | Bob | ...
I also expect unions might be a possible solution - but other then just joining all the possible tables and parsing the columns to match up properly (which it could be as many as 20 tables) I'd rather not. This solution is also a bit of a nusience since the columns don't always match in a good way (e.g. the Person table doesn't have similar columns to the Address table)
I don't think the left join idea is that bad if you just ignore object type.
Since each ID is unique you don't need to look at type at all if you use coalesce. So to use #TT model as an example:
SELECT bt.*,
COALESCE(P.f1, L.f1, C.f1) AS f1,
-- ...,
COALESCE(P.fn, L.fn, C.fn) AS fn
FROM
base_table AS bt
LEFT JOIN Person AS P ON P.entity_id = bt.entity_id
LEFT JOIN [Legal Person] AS L ON L.entity_id = bt.entity_id
LEFT JOIN Counterpart AS C ON C.entity_id = bt.entity_id
Depending on your data size and indexes this might perform faster or the same as TT's example -- remember there is only 1 select with N joins while TT's has N selects, 2N joins. It really depends on your data.
If there is some field (fz) that does not show up in all types then you just don't inlcude that in the coalesce clause.
I think this style might be easier to maintain and understand and will be the same or faster as TT code.
What you probably want to do is the following: for each possible detail-table (ie the possible values in [object.value]), write a query that only links with that one detail-table and have a WHERE clause to restrict to the proper entities. Then do a UNION ALL for all those queries.
Say you have Person, Legal Person and Counterpart as possible values in [object.type]. Suppose the detail-tables have the same names. You can write:
SELECT
bt.*,
dt.f1,
-- ...,
dt.fn
FROM
base_table AS bt
INNER JOIN object AS o ON o.id = bt.role
INNER JOIN Person AS dt ON dt.entity_id = bt.entity_id
WHERE
o.type='Person'
UNION ALL
SELECT
bt.*,
dt.f1,
-- ...,
dt.fn
FROM
base_table AS bt
INNER JOIN object AS o ON o.id = bt.role
INNER JOIN [Legal Person] AS dt ON dt.entity_id = bt.entity_id
WHERE
o.type='Legal Person'
UNION ALL
SELECT
bt.*,
dt.f1,
-- ...,
dt.fn
FROM
base_table AS bt
INNER JOIN object AS o ON o.id = bt.role
INNER JOIN Counterpart AS dt ON dt.entity_id = bt.entity_id
WHERE
o.type='Counterpart'

Shortcut for adding table to column name SQL-server 2014

Stupidly simple question, but I just don't know what to google!
If I create a query like this:
Select id, data
from table1
Now I want to join with table2. I can immediately see that the id column is no longer unique and I have to change it to
table1.id
Is there any smart way (like a keyboard-shortcut) to do this, instead of manually adding table1 to every column? Either before I add the Join to secure that all columns will be unique, or after with suggestions based on the different possible tables.
No, there is no helper.
But do not you can alias the table name:
select x.Col1, y.Col2
from ALongTableName x
inner join AReallyReallyLongTableName y on x.Id = y.OtherId
which can also make queries clearer, and is very much necessary when doing self joins.
First of all, you should start using aliases:
SQL aliases are used to give a database table, or a column in a table,
a temporary name.
Basically aliases are created to make column names more readable.
This will narrow down your problem and make your code maintenance easier. If that's not enough, I guess you could start using auto-completion tools, such as these:
SQL Complete
SQL Prompt
ApexSQL Complete
These have your desired functionality, however, they do not always work as expected (at least for me).
Oh! You can use alias table name. Like this:
SELECT A.ID, A.data
FROM TableA A
INNER JOIN TableB B
ON A.ID = B.ID
You just only use A. or B. if two table have same this column selected. If they different, you don't need: Like this:
SELECT A.ID, data -- if Table B not have column data
FROM TableA A
INNER JOIN TableB B
ON A.ID = B.ID
Or:
Select A.*, B.ID
FROM TableA A
INNER JOIN TableB B
ON A.ID = B.ID

SQL Same Column in one row

I have a lookup table that has a Name and an ID in it. Example:
ID NAME
-----------------------------------------------------------
5499EFC9-925C-4856-A8DC-ACDBB9D0035E CANCELLED
D1E31B18-1A98-4E1A-90DA-E6A3684AD5B0 31PR
The first record indicates and order status. The next indicates a service type.
In a query from an orders table I do the following:
INNER JOIN order.id = lut.Statusid
This returns the 'cancelled' name from my lookup table. I also need the service type in the same row. This is connected in the order table by the orders.serviceid How would I go about doing this?
It Cancelled doesnt connect to 31PR.
Orders connects to both. Orders has 2 fields in it called Servicetypeid and orderstatusid. That is how those 2 connect to the order. I need to return both names in the same order row.
I think many will tell you that having two different pieces of data in the same column violates first normal form. There is a reason why having one lookup table to rule them all is a bad idea. However, you can do something like the following:
Select ..
From order
Join lut
On lut.Id = order.StatusId
Left Join lut As l2
On l2.id = order.ServiceTypeId
If order.ServiceTypeId (or whatever the column is named) is not nullable, then you can use a Join (inner join) instead.
A lot of info left out, but here it goes:
SELECT orders.id, lut1.Name AS OrderStatus, lut2.Name AS ServiceType
FROM orders
INNER JOIN lut lut1 ON order.id = lut.Statusid
INNER JOIN lut lut2 ON order.serviceid = lut.Statusid

Iterating 1 row at a time with massive amounts of links/joins

Ok, basically what is needed is a way to have row numbers while using a lot of joins and having where clauses using these rownumbers.
such as something like
select ADDRESS.ADDRESS FROM ADDRESS
INNER JOIN WORKHISTORY ON WORKHISTORY.ADDRESSRID=ADDRESS.ADDRESSRID
INNER JOIN PERSON ON PERSON.PERSONRID=WORKHISTORY.PERSONRID
WHERE PERSONRID=<some number> AND WORKHISTORY.ROWNUMBER=1
ROWNUMBER needs to be generated for this query on that one table though. So that if we want to access the second WORKHISTORY record's address, we could just go WORKHISTORY.ROWNUMBER=2 and if say we had two address's that matched, we could cycle through the addresses for one WORKHISTORY record using ADDRESS.ROWNUMBER=1 and ADDRESS.ROWNUMBER=2
This should be capable of being an automatically generated query. Thus, there could be more than 10 inner joins in order to get to the relevant table, and we need to be able to cycle through each table's record independently of the rest of the tables..
I'm aware there is the RANK and ROWNUMBER functions, but I'm not seeing how it will work for me because of all the inner joins
note: in this example query, ROWNUMBER should be automatically generated! It should never be stored in the actual table
Can you use a temp table?
I ask because you can write the code like this:
select a.field1, b.field2, c.field3, identity (int, 1,1) as TableRownumber into #temp
from table1 a
join table2 b on a.table1id = b.table1id
join table3 c on b.table2id = c.table2id
select * from #temp where ...

Resources