SQL Select from case or IF...Then logic? - sql

I'm trying to eliminate making a post pass into the db and in doing so have encountered this problem.
The scenario is that I need to join onto a table that has many rows per item. Think dated values, Item x has a value of n at date y. There's also a row for the same item at date x and z.
I have no influence or power to change this table.
What I'm trying to do is something along the lines of this:
Select
table1.Name as name,
table1.date as date,
if (date > x)
select table2.value as value,
table2.othervalue as otehrvalue
from table 2
where table1.x = table2.x
from table1
but I'm running into difficulties.
I've successfully run the test conditions with strings so if date > x display a string and so on but I can't seem to get the select to work(if possible) and I'm not sure where I'm going wrong. I've looked online and not found much to go on which leads me to think I could be barking up the wrong tree...
We're using Advantage DB.
Any help appreciated
Edited as I'd liek to return more than one value from the sub query...

Use a CASE expression:
select table1.Name as name,
table1.date as date,
case when date > x then
(select table2.value
from table2
where table1.x = table2.x)
end as value
from table1
Will return NULL when date <= x.
Note, if the sub-select returns more than one row you'll get an error!

You can also use case...when with a left outer join:
Select
table1.Name as name,
table1.date as date,
case when table1.date > x
then table2.value
else null
end as value
from table1
left join table2
on table1.x = table2.x;
Edit, Re : Conditionally return multiple columns from table 2
Yes, you should be able to do this for multiple columns, by using the date > x as a join condition, and retaining the left outer join (this will again project NULL's for any failed joins), and finally using COALESCE to revert any NULLs back to the Table1 condition:
Select
table1.Name as name,
table1.date as date,
COALESCE(table2.x, table1.x) as x,
COALSECE(table2.y, table1.y) as y
from table1
left join table2
on table1.x = table2.x AND table1.date > x;

Related

How to JOIN and get data from either table based on specific logics?

Let's say I have 2 tables as shown below:
Table 1:
Table 2:
I want to join the 2 tables together so that the output table will have a "date" column, a "hrs_billed_v1" column from table1, and a "hrs_billed_v2" column from table2. Sometimes a date only exists in one of the tables, and sometimes a date exists in both tables. If a date exists in both table1 and table2, then I want to allocate the hrs_billed_v1 from table1 and hrs_billed_v2 from table2 to the output table.
So the ideal result will look like this:
I've tried "FULL OUTPUT JOIN" but it returned some null values for "date" in the output table. Below is the query I wrote:
SELECT
DISTINCT CASE WHEN table1.date is null then table2.date WHEN table2.date is null then table1.date end as date,
CASE WHEN table1.hrs_billed_v1 is null then 0 else table1.hrs_billed_v1 END AS hrs_billed_v1,
CASE WHEN table2.hrs_billed_v2 is null then 0 else table2.hrs_billed_v2 END AS hrs_billed_v2
FROM table1
FULL OUTER JOIN table2 ON table1.common = table2.common
Note that the "common" column where I use to join table1 and table2 on is just a constant string that exists in both tables.
Any advice would be greatly appreciated!
A full join is indeed what you want. I think that would be:
select
common,
date,
coalesce(t1.hrs_billed_v1, 0) as hrs_billed_v1,
coalesce(t2.hrs_billed_v2, 0) as hrs_billed_v2
from table1 t1
full join table2 t2 using (common, date)
Rationale:
you don't show what common is; your data indicates that you want to match rows of the same date - so I put both in the join condition; you might need to adapat that
there should really be no need for distinct
coalesce() is much shorter than the case expressions
using () is handy to express the join condition when the columns to match have the same name in both tables

sql - ignore duplicates while joining

I have two tables.
Table1 is 1591 rows. Table2 is 270 rows.
I want to fetch specific column data from Table2 based on some condition between them and also exclude duplicates which are in Table2. Which I mean to join the tables but get only one value from Table2 even if the condition has occurred more than time. The result should be exactly 1591 rows.
I tried to make Left,Right, Inner joins but the data comes more than or less 1591.
Example
Table1
type,address,name
40,blabla,Adam
20,blablabla,Joe
Table2
type,currency
40,usd
40,gbp
40,omr
Joining on 'type'
Result
type,address,name,currency
40,blabla,name,usd
20,blblbla,Joe,null
try this it has to work
select *
from
Table1 h
inner join
(select type,currency,ROW_NUMBER()over (partition by type order by
currency) as rn
from
Table2
) sr on
sr.type=h.type
and rn=1
Try this. It's standard SQL, therefore, it should work on your rdbms system.
select * from Table1 AS t
LEFT OUTER JOIN Table2 AS y ON t.[type] = y.[type] and y.currency IN (SELECT MAX(currency) FROM Table2 GROUP BY [type])
If you want to control which currency is joined, consider altering Table2 by adding a new column active/non active and modifying accordingly the JOIN clause.
You can use outer apply if it's supported.
select a.type, a.address, a.name, b.currency
from Table1 a
outer apply (
select top 1 currency
from Table2
where Table2.type = a.type
) b
I typical way to do this uses a correlated subquery. This guarantees that all rows in the first table are kept. And it generates an error if more than one row is returned from the second.
So:
select t1.*,
(select t2.currency
from table2 t2
where t2.type = t1.type
fetch first 1 row only
) as currency
from table1 t1;
You don't specify what database you are using, so this uses standard syntax for returning one row. Some databases use limit or top instead.

SQL aggregate function returning inflated values on joined table

I'm breaking my head here where I'm going wrong.
The following query:
SELECT SUM(table1.col1) FROM table1
returns value x.
And the following query:
SELECT SUM(table1.col1) FROM table2 RIGHT OUTER JOIN table1 ON table2.ID = table1.ID
returns value y. (I need the Join for the other data of table2). Why is the 2nd example returning a different value than in the first?
Make life easier on yourself, your colleagues that will support your code, and your clients by temporarily ignoring the existence of RIGHT OUTER JOIN. Use Table1 as the "from table" instead of table2.
Then, If aggregating, you will often find it necessary to do this BEFORE joining, so that the numbers are accurate. e.g.
SELECT T1.SUMCOL1
FROM (
SELECT id, SUM(col1) as SUMCOL1 FROM Table1 GROUP BY id
) T1
LEFT OUTER JOIN table2 T2 on T1.id = T2.ID
Obvious answer is because table2 is many to table1's one. That is, there are multiple rows in table2 for one id in table1. You may also be eliminating rows from table1 if the id isn't present in table2.
Compare:
SELECT COUNT(*) FROM table1
To:
SELECT COUNT(*) FROM table2 RIGHT OUTER JOIN table1 ON table2.ID = table1.ID
If you get different results, you're aggregating duplicates or eliminating rows from table1.
If you want to avoid this, you'll need to use a subquery.

Pretty simple SQL query using another query

I need to select all IDs from one table with columns ID and X, WHERE X = 'Y'. For each of those IDs, I need to look up some stuff in a different table:
If the ID does not exist, it gets no row in the final result.
If the ID does exist, I want to do some logic to figure out if it gets a row. For simplicity, assume that the logic is: if column Q > 0.
So the final result is simply a column of IDs, throwing out some because they are disqualified for one of two reasons as above.
thanks.
This is what JOINs are made for.
SELECT table1.* FROM table1
INNER JOIN table2
ON table1.ID = table2.table1_ID
AND table2.Q > 0;
This will select all records in table1 (which have IDs) and then remove any records that do not have a matching record in table2 or do not have a Q > 0.
If I got your question right this might be what you are looking for:
select id from your_table
where X = 'Y'
and id in (select id from other_table where Q > 0)
You can use a subquery:
SELECT id FROM table1 WHERE table1.id IN (SELECT q FROM table2 WHERE table2.q > 0) AND table1.x='y'
SQL will check to see if the results from the outermost query are in the subquery (the part in parentheses) and won't return anything if they aren't.

Getting distinct rows from a left outer join

I am building an application which dynamically generates sql to search for rows of a particular Table (this is the main domain class, like an Employee).
There are three tables Table1, Table2 and Table1Table2Map.
Table1 has a many to many relationship with Table2, and is mapped through Table1Table2Map table. But since Table1 is my main table the relationship is virtually like a one to many.
My app generates a sql which basically gives a result set containing rows from all these tables. The select clause and joins dont change whereas the where clause is generated based on user interaction. In any case I dont want duplicate rows of Table1 in my result set as it is the main table for result display. Right now the query that is getting generated is like this:
select distinct Table1.Id as Id, Table1.Name, Table2.Description from Table1
left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id)
left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id)
For simplicity I have excluded the where clause. The problem is when there are multiple rows in Table2 for Table1 even though I have said distinct of Table1.Id the result set has duplicate rows of Table1 as it has to select all the matching rows in Table2.
To elaborate more, consider that for a row in Table1 with Id = 1 there are two rows in Table1Table2Map (1, 1) and (1, 2) mapping Table1 to two rows in Table2 with ids 1, 2. The above mentioned query returns duplicate rows for this case. Now I want the query to return Table1 row with Id 1 only once. This is because there is only one row in Table2 that is like an active value for the corresponding entry in Table1 (this information is in Mapping table).
Is there a way I can avoid getting duplicate rows of Table1.
I think there is some basic problem in the way I am trying to solve the problem, but I am not able to find out what it is. Thanks in advance.
Try:
left outer join (select distinct YOUR_COLUMNS_HERE ...) SUBQUERY_ALIAS on ...
In other words, don't join directly against the table, join against a sub-query that limits the rows you join against.
You can use GROUP BY on Table1.Id ,and that will get rid off the extra rows. You wouldn't need to worry about any mechanics on join side.
I came up with this solution in a huge query and it this solution didnt effect the query time much.
NOTE : I'm answering this question 3 years after its been asked but this may help someone i believe.
You can re-write your left joins to be outer applies, so that you can use a top 1 and an order by as follows:
select Table1.Id as Id, Table1.Name, Table2.Description
from Table1
outer apply (
select top 1 *
from Table1Table2Map
where (Table1Table2Map.Table1Id = Table1.Id) and Table1Table2Map.IsActive = 1
order by somethingCol
) t1t2
outer apply (
select top 1 *
from Table2
where (Table2.Id = Table1Table2Map.Table2Id)
) t2;
Note that an outer apply without a "top" or an "order by" is exactly equivalent to a left outer join, it just gives you a little more control. (cross apply is equivalent to an inner join).
You can also do something similar using the row_number() function:
select * from (
select distinct Table1.Id as Id, Table1.Name, Table2.Description,
rowNum = row_number() over ( partition by table1.id order by something )
from Table1
left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id)
left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id)
) x
where rowNum = 1;
Most of this doesn't apply if the IsActive flag can narrow down your other tables to one row, but they might come in useful for you.
To elaborate on one point: you said that there is only one "active" row in Table2 per row in Table1. Is that row not marked as active such that you could put it in the where clause? Or is there some magic in the dynamic conditions supplied by the user that determines what's active and what isn't.
If you don't need to select anything from Table2 the solution is relatively simply in that you can use the EXISTS function but since you've put TAble2.Description in the clause I'll assume that's not the case.
Basically what separates the relevant rows in Table2 from the irrelevant ones? Is it an active flag or a dynamic condition? The first row? That's really how you should be removing duplicates.
DISTINCT clauses tend to be overused. That may not be the case here but it sounds like it's possible that you're trying to hack out the results you want with DISTINCT rather than solving the real problem, which is a fairly common problem.
You have to include activity clause into your join (and no need for distinct):
select Table1.Id as Id, Table1.Name, Table2.Description from Table1
left outer join Table1Table2Map on (Table1Table2Map.Table1Id = Table1.Id) and Table1Table2Map.IsActive = 1
left outer join Table2 on (Table2.Id = Table1Table2Map.Table2Id)
If you want to display multiple rows from table2 you will have duplicate data from table1 displayed. If you wanted to you could use an aggregate function (IE Max, Min) on table2, this would eliminate the duplicate rows from table1, but would also hide some of the data from table2.
See also my answer on question #70161 for additional explanation