Why does a join add duplicate column names - sql

I have two tables and when I do a join it joins the tables together side by side not doing row based if same columns and not sure why this could be.
+--------+--------+--------+
| VALUE| DAY | Color
+--------+--------+--------+
|20 |MON | BLUE|
+--------+--------+--------+
+--------+--------+--------+
| VALUE| DAY | Color |
+--------+--------+--------+
|20 |MON | RED |
+--------+--------+--------+
I am getting the below table when join on key VALUE the above two inputs:
+--------+--------+--------+--------+--------+
| VALUE| DAY | Color |DAY |Color |
+--------+--------+--------+--------+--------+
|20 |MON | BLUE|Mon |Red |
+--------+--------+--------+--------+--------+
I want to get something like below like row based
+--------+--------+--------+
| VALUE| DAY | Color |
+--------+--------+--------+
|20 |MON | BLUE |
|20 |MON | RED |
+--------+--------+--------+

You need to provide a sample SQL you are using, none the less you need to use union instead of join.
SELECT value, day, color FROM table1
UNION ALL
SELECT value, day, color FROM table2;

The JOIN doesn't add duplicate columns or column names. It basically selects all columns from all joined tables (unless you provide those columns that should be selected). In your case, the column names in both tables are identic. If you want to prevent this, you could of course rename the columns. If you don't want to rename them in the DB, but just want to have different names in your result set, you can use aliases, meaning something like:
SELECT t1.value AS value1, t1.day AS day1, t1.color AS color1,
t2.value AS value2, t2.day AS day2, t2.color AS color2
FROM table1 JOIN table2 ON t1.value = t2.value;
If you also want to select the entries of table1 if no identic in table2 having the same value exists or vice versa, you can use LEFT JOIN or RIGHT JOIN. Have a look here how JOIN works: explanation
The output you've shown lets assume you don't even want to join your tables at all, but just do UNION, meaning select both the entries of table1 and table2. Basically, this can be done like this:
SELECT value, day, color FROM table1
UNION
SELECT value, day, color FROM table2;
You have to check if you want to select identic entries twice (in this case, you would use UNION ALL instead of UNION). And you maybe should add a meaningful WHERE clause and/or GROUP BY conditions.

Related

How can I get all distinct combinations from multiple rows that share differnt id numbers?

I have about 150 names, that are linked together by 160K different id numbers. I'd like to get the distinct combonations of these names. So for example, the names John and Kate can be linked thousands of times by different Ids, but I just want to know that John and Kate are a linked pair. In some cases there are more than 2 names linked. For example, this is what my table currently looks like:
ID| Name|
1| Mike|
1|John |
2|Kate |
2|John |
3|Frank|
3|Ted |
3|Blue |
4|John |
4|Mike |
5|John |
5|Kate |
6|John |
6|Kate |
In this case, we have combinations based on the various ids. We also have redundant combinations; for example id1 and 4, as well as 2, 5, 6. I'd like the output just to give me the distinct combos, regardless of order they appear in the table. The output would look like:
Name1| Name2 |Name3
Mike | John |
Kate | John |
Frank| Ted | Blue
4 would be omitted because its the frist row, and 5, 6 would be omitted since its covered by the second row
For linked pairs, you would use a self-join:
select distinct t1.name, t2.name
from t t1 join
t t2
on t1.id = t2.id and t1.name < t2.name;
I am not clear if that solves your entire problem, though.

SQL Help - Join small lookup table where not all columns are required (and an other option)

I have one large table with transactions and a smaller lookup table with values I want to add based on 4 common columns. The trick here is not every combination of these 4 columns will exist in the lookup table and there are scenarios where I want it to stop checking and accept the match instead of going to the next column. I also have an "Other" option to default to if it doesn't match any of the options.
Table structures are something like this:
transaction_table
country, trans_id, store_type, store_name, channel, browser, purchase_amount, currency
lookup_table
country, store_name, channel, browser, trans_fee
The data could be something like this:
transaction_table:
country| trans_id| store_type |store_name |channel |browser |amt |currency
US | 001 | Big Box | Target | B&M |N/A |1.45 |USD
US | 002 | Big Box | Target | Online |Chrome |1.79 |USD
US | 003 | Small | Bob's Store| B&M |N/A |2.50 |USD
US | 004 | Big Box | Walmart | B&M |N/A |1.12 |USD
US | 005 | Big Box | Walmart | Online |Firefox |3.79 |USD
US | 006 | Big Box | Amazon | Online |IE |4.54 |USD
US | 007 | Small | Jim's Plc | B&M |IE |2.49 |USD
lookup_table:
country|store_name |channel |browser |trans_fee
US |Target |B&M |N/A |0.25
US |Target |Online | |0.15
US |Walmart | | |0.30
US |Other | | |0.45
So looking at the lookup_table data:
Row 1 is very specific and would be a match on all 4 of the join
columns.
Row 2 would not care what browser was used to shop at Target so
regardless of the "browser" value, the trans_fee should come back
the same (other stores may care though).
Row 3 is saying any transaction with a country='US' and the
store_name='Walmart', regardless of the rest of the join columns
would have the same trans_fee
Row 4 is the "other" scenario where it should look first at the
store_name column and if it doesn't find a match, go to Other.
The lookup_table data can change and may end up being time dependent (start_date and end_date columns added) so it really wouldn't be a good candidate for a long, complex CASE statement.
I was thinking of a combination of checking each column with an IF IN statement but I'm hoping there's a more straightforward conditional join type statement I can use to go column by column and have an other option.
Thanks!
edit: I didn't specify this but I want to basically return all of the data from transaction_table and add the corresponding trans_fee to each line.
You will need to use a conditional JOIN.
Something like this
SELECT *
FROM lookup_table
LEFT OUTER JOIN transaction_table
ON CASE WHEN lookup_table.store_name IS NOT NULL
THEN transacton_table.store_name = lookup_table.store_name END
Such partial matching is tricky. And your problem is not really that well set up. You seem to have NULLs in some columns and general values in others.
In any case, you can solve this by matching what you can and then using order by to get the best match. In your case, I think this looks like this:
select tt.*,
(select trans_fee
from lookup l
where l.country = tt.country and
l.store_name in ('other', tt.store_name) and
(l.channel = tt.channel or l.channel is null) and
(l.browser = tt.browser or l. browser is null)
order by (case when l.store_name = tt.store_name then 1 else 2 end),
(case when l.channel = tt.channel then 1 else 2 end),
(case when l.browser = tt.browser then 1 else 2 end)
fetch first 1 row only
) as trans_fee
from transaction_table tt;
This is generic SQL. But the same idea should work in any database.

New column referencing second table - do I need a join?

I have two tables (first two shown) and need to make a third from the first two - do I need to do a join or can you reference a table without joining?
The third table shown is the desired output. Thanks for any help!
| ACC | CALL DATE | | |
+-----+-----------+--+--+
| 1 1 | 2/1/18 | | |
+-----+-----------+--
+-----+---------------+--+--+
| ACC | PURCHASE DATE | | |
+-----+---------------+--+--+
| 1 1 | 1/1/18 | | |
+-----+---------------+--+--+
+-----+-----------+----------------------+--+
| ACC | CALL DATE | PRIOR MONTH PURCHASE | |
+-----+-----------+----------------------+--+
| 1 1 | 2/1/18 | YES | |
+-----+-----------+----------------------+--+
Of course you can have a query that references multiple tables without joining. union all is an example of an operator that does that.
There is also the question of what you mean by "joining" in the question. If you mean explicit joins, there are ways around that -- such as correlated subqueries. However, these are implementing some form of "join" in the database engine.
As for your query, you would want to use exists with a correlated subquery:
select t1.*,
(case when exists (select 1
from table2 t2
where t2.acc = t1.acc and
datediff(month, t2.purchase_date, t1.call_date) = 1
)
then 'Yes' else 'No'
end) as prior_month_purchase
from table1 t1;
This is "better" than a join because it does not multiply or remove rows. The result set has exactly the rows in the first table, with the additional column.
The syntax assumes SQL Server (which was an original tag). Similar logic can be expressed in other databases, although date functions are notoriously database-dependent.
Lets check the options,
Say if you were to create a new third table on the basis of the data in first two, then every update/inserts/deletes to either of the tables should also propagate into the third table as well.
Say you instead have a view which does what you need, there isnt a need to maintain that third table and also gets you the data needed from the first two each time you query it.
create view third_table as
select a.acc,a.call_date,case when dateadd(mm,-1,a.call_date)=b.purchase_date then 'Yes' else 'No end as prior_month_purchase
from first_table a
left join second_table b
on a.acc=b.acc

Create VIEW (count duplicate values in column)

I have little project with SQL database which has table with some column.
Question: How create View in SQL Server, which count how many duplicate values I have in column and show that number in next column.
Here below you can see result which I want to take.
|id|name|count|
|1 |tom | |
|2 |tom | |
|3 |tom | |
| | | 3 |
|4 |leo | |
| | | 1 |
A view is simply a select statement with the words CREATE VIEW AS before the SELECT. This allows for example, 1 person (DBA) to maintain (create/alter) complex views, while another person (developer) only has the rights to select from them.
So to use #Stidgeon's answer (below):
CREATE VIEW MyCounts
AS
SELECT name, COUNT(id) AS counts
FROM table
GROUP BY name
and later you can query
Select * from MyCounts where counts > 1 order by name
or whatever you need to do. Note that order by is not allowed in views in SQL SERVER.
You can do what you want with grouping sets:
select id, name, count(*)
from t
group by grouping sets ((id, name), (name));
The group by on id, name is redundant; the value should always be "1". However, this allows the use of grouping sets, which is a convenient way to phrase the query.
Looks like you just want to count how many entries you have for each 'name', in which case you just need to do a simple COUNT query:
CREATE VIEW view_name AS
SELECT name, COUNT(id) AS counts
FROM table
GROUP BY name
The output in your case would be:
name counts
--------------
Tom 3
Leo 1

How to read previous values in sql

I have a table where the IDs , Reference IDs and amounts are stored. The problem is that for the rows where reference IDs is set the amount is missing. I need to read the rows where reference_id = ID and read the amount and set the value (like it is shown in Table2).
+--+------------+------+
|ID|Reference ID|Amount|
+--+------------+------+
|1 | |300 |
+--+------------+------+
|2 |1 | |
+--+------------+------+
I want to be able to show:
Table 2
+--+------------+------+
|ID|Reference ID|Amount|
+--+------------+------+
|1 | |300 |
+--+------------+------+
|2 |1 |300 |
+--+------------+------+
Anyone has any idea whats the best way to find this missing value?
Best Regards.
MEJ
I think you want a self-join:
select t1.id, t1.referenceid, coalesce(t2.amount, t1.amount) as amount
from table1 t1 left outer join
table1 t2
on t1.id = t2.referenceid;
I think you want a hierarchical query:
select id, ref_id, connect_by_root amount
from <your table>
connect by prior id = ref_id
start with ref_id is null;
SQL Fiddle.
More about the connect_by_root operator in the documentation.
This allows for multiple levels since it always goes back to the root for the amount. But that kind of assumes that the child records never have an amount themselves, or it can be ignored. You can use nvl to the the child value if it is set, but children of that will still go back to the root. You can add an amount null check to the conditions if you want to show the previous value:
select id, ref_id, connect_by_root amount as amount
from <your table>
connect by prior id = ref_id and amount is null
start with ref_id is null or amount is not null
order by id;
SQL Fiddle.