Conditional Join SQL Server - sql

I have a scenario as below:
Source1
Column1
Column2
Source2
Column1
Column2
Output - I need a view;
All the records in Source1 where Column2 is not empty must be in the view.
All the records in Source1 where Column2 is empty must be joined with Source2 (on Column1, the reference between both tables). Wherever it finds a match then Column2 of Source2 should also be included in the view.
Any pointers please..

The easiest way I see is with an union of both queries:
SELECT * FROM Source1 WHERE Column2 IS NOT NULL
UNION
SELECT S2.* FROM Source1 S1
INNER JOIN Source2 S2
ON S1.Column1 = S2.Column1
WHERE S1.Column2 IS NULL

So to recap: only include lines where Source1.Column2 is populated, and include Column2 of Source2 as well if that is also populated?
What you're looking for is a LEFT JOIN. Learn it and love it, because it's one of the handiest things in the whole of SQL.
SELECT s1.Column1, s1.Column2, s2.Column2
FROM source1 s1
LEFT JOIN source2 s2 ON s1.Column1 = s2.Column1
WHERE s1.Column2 IS NOT NULL

Use an outer join between Source1 and Source2.
The specification is a little loose. Did you want the Column2 from Source2 to be returned as a separate (third) column, or did you want the value from that in the second column, replacing the "empty" value of Column2 from Source1?
What are the datatypes of Column2 in Source1 and Source2? Is that character type, numeric, datetime?
How do you define "empty"? For character types, does that include both NULL values and zero length strings?
Also, what is the cardinality of the relationship between the tables, is it one to one, one to many (which way). Is it it mandatory, or possibly one to zero?
Assuming you want all the rows (if there are multiple rows from Source2 that match a row from Source1, and assuming you want a third column, and assuming the datatype of Column2 is character, and assuming "empty" means NULL or zero-length string (that's a whole lot of assumptions)... then something like this:
SELECT s.column1
, s.column2
, IF(IFNULL(s.column2,'')='',t.column2,'') AS t_column2
FROM source1 s
LEFT
JOIN source2 t
ON t.column1 = s.column1
AND IFNULL(s.column2,'') = ''
ORDER BY s.column1, 2, 3
... will return a result that meets the specification. That query can be altered/tweaked to adjust to a tighter (more precise) specification.
EDIT
Ooops.
The example query above was based on yet another assumption: that this was specific to MySQL.
The syntax of the statement above won't "work" in other databases. Here's an equivalent statement, using more ANSI-standards compliant syntax:
SELECT s.column1
, s.column2
, CASE WHEN s.column2 IS NULL OR s.column2 = ''
THEN t.column2
ELSE ''
END AS t_column2
FROM source1 s
LEFT
JOIN source2 t
ON t.column1 = s.column1
AND (s.column2 IS NULL OR s.column2 = '')
ORDER BY s.column1, s.column2
FOLLOWUP
Added an example that demonstrates the behavior:
SQL Fiddle here: http://sqlfiddle.com/#!9/113e6/1
Setup tables and sample rows:
CREATE TABLE source1
( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY
, column1 INT
, column2 VARCHAR(8)
);
CREATE TABLE source2
( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY
, column1 INT
, column2 VARCHAR(8)
);
INSERT INTO source1 (id, column1, column2) VALUES
(1,NULL,NULL)
,(2,NULL,'foo')
,(3,113,'fee')
,(4,114,'fi')
,(5,115,'')
,(6,116,NULL)
,(7,122,'fo')
,(8,122,'fum')
;
INSERT INTO source2 (id, column1, column2) VALUES
(21,NULL,'doh')
,(22,113,'rey')
,(23,113,'mii')
,(24,114,'fah')
,(25,115,'sew')
,(26,115,'lah')
,(27,116,NULL)
,(28,116,'')
,(29,116,'tea')
,(30,116,'doh')
;
Example query (same as query above):
SELECT s.column1
, s.column2
, IF(IFNULL(s.column2,'')='',t.column2,'') AS t_column2
FROM source1 s
LEFT
JOIN source2 t
ON t.column1 = s.column1
AND IFNULL(s.column2,'') = ''
ORDER BY s.column1, 2, 3
Example query - same as query above PLUS extra id columns
SELECT s.column1
, s.column2
, IF(IFNULL(s.column2,'')='',t.column2,'') AS t_column2
-- ---------------
, s.id AS s_id
, t.id AS t_id
-- ---------------
FROM source1 s
LEFT
JOIN source2 t
ON t.column1 = s.column1
AND IFNULL(s.column2,'') = ''
ORDER BY s.column1, 2, 3
Returns:
column1 column2 t_column2 s_id t_id
------- ------- --------- ------ --------
(NULL) (NULL) (NULL) 1 (NULL)
(NULL) foo 2 (NULL)
113 fee 3 (NULL)
114 fi 4 (NULL)
115 lah 5 26
115 sew 5 25
116 (NULL) (NULL) 6 27
116 (NULL) 6 28
116 (NULL) doh 6 30
116 (NULL) tea 6 29
122 fo 7 (NULL)
122 fum 8 (NULL)
Note that this example includes "duplicate" values in column1, of both source1 and source2, and shows the results that are returned. (The s_id and t_id columns are included to aid in deciphering the rows returned.)

Related

How to get unique value from sql table based on charachter length of value

Here I have two columns like below example column1 & column2 in sql table and i want to get unique value row on the basis of column2 column value from table
Below example of dummy table
Column1 Column2
---------- -------------
1001 ab
1001 abc
1001 abcd
2001 wxyz
2001 wxy
2001 wx
In above example value starting from a & another value starting from w in Column2
On the basis of same value character length, i want to get result like below
Output:
Column1 Column2
---------- -----------
1001 abcd
2001 wxyz
Thanks in advance of help :)
If I understood you correctly, you want the highest length (you didn't say what to do when there are two with the same length) but basically you want something like this:
SELECT * FROM YourTable
WHERE (column1,len(column2)) IN(select Column1,max(len(column2))
FROM YourTable
GROUP BY Column1)
If you are looking if your values in column2 are somewhere included in other rows, in other words: If you are looking for rows with combinations of characters which are unique on their own, this might be your solution:
CREATE TABLE TestTable(Column1 INT,Column2 VARCHAR(100));
INSERT INTO TestTable VALUES
(1001,'ab')
,(1001,'abc')
,(1001,'abcd')
,(2001,'wxyz')
,(2001,'xyz')
,(2001,'yz');
SELECT *
FROM TestTable
WHERE NOT EXISTS(SELECT 1
FROM TestTable AS x
WHERE x.Column1=TestTable.Column1
AND LEN(x.Column2)>LEN(TestTable.Column2)
AND x.Column2 LIKE '%' + TestTable.Column2 + '%'
)
DROP TABLE TestTable;

Union columns and remove blanks

I have a table which appears like this (I've shortened it for example purposes)
no no19 no68
3387034694344500
3387452540705400
3388486878919450
3371522572594880
3372232397709690
3373608476884750
3382142940562320
3382142940562320
3383084144363070
so no, no19 and no68 are 3 different columns, but the data in column 'no19' starts in the next row after data in column 'no' ends.
As between these columns I have a lot more data, I would like to create a readable table. I have merged these columns into one, using this code:
CREATE TABLE MULTICURRENCY_CHECK
(
TOKEN varchar(255)
)
INSERT INTO MULTICURRENCY_CHECK
(
TOKEN
)
SELECT no FROM book1
UNION ALL
SELECT no19 FROM book1
UNION ALL
SELECT no68 FROM book1
The problem is, the result I got looks like this:
TOKEN
3387034694344500
3387452540705400
3388486878919450
3371522572594880
3372232397709690
3373608476884750
3382142940562320
3382142940562320
3383084144363070
So there are blank rows between in column TOKEN. I've tried to delete them, but it by simple delete command but it's not working (tried those two below):
delete from multicurrency_check where TOKEN = ' '
delete from multicurrency_check where TOKEN is NULL
Perhaps there is a different way I should deal with this table, maybe quicker? As the original table looks like these (just sample data)
no a b no19 c d no68
3387034694344500 data1 data4
3387452540705400 data2 data5
3388486878919450 data3 data6
3371522572594880 data7 data10
3372232397709690 data8 data11
3373608476884750 data9 data12
3382142940562320
3382142940562320
3383084144363070
so what I would like to have in the end is table like this:
| TOKEN | a | b | c | d
where token is a merge of no, no19 and no68, and then folowwing a,b,c,d columns with data matching appropriate id from TOKEN column (a,b,c,d can be null)
You could delete them in your UNION query like:
SELECT no FROM table WHERE no IS NOT NULL
UNION
SELECT no19 FROM table WHERE no19 IS NOT NULL
UNION
SELECT no68 FROM table where no68 IS NOT NULL
You could also use COALESCE() instead of union since a column only contains data when the other's are null:
SELECT COALESCE(no, no19, no68) FROM table
Instead of putting these values in their own table, you could start with the above queries and build off of them. Say you want to also bring in A,B or C,D into the results:
SELECT COALESCE(no, no19, no68) as newno, COALESCE(a,c) as ac, COALESCE(b,d) as bd FROM table;
As to why your DELETE didn't work, perhaps those NULL's aren't NULL. Perhaps they hold a TAB character or 50 spaces? In which case #sidux's comment on your Q would do the trick. Trimming the field and looking where its value is =''.
Maybe something like this:
select
isnull(no,'')+isnull(no19,'')+isnull(no68,''),
a,b,c,d
from book1
That should concatenate all token in a row (and only one from no, no19 and no68 will have a value).
I created a table 'foo' with 3 text columns, like so:
column1 column2 column3
------- ------- -------
row1 3371522572594880 3373608476884750
row2 asdfasdf asdfasdf
row3 3387452540705400 3388486878919450
Then executed the query
select token from(
select column1 as token from foo where column1 != ''
union all
select column2 as token from foo where column2 != ''
union all
select column3 as token from foo where column3 != ''
)
And got the result:
token
3371522572594880
asdfasdf
3387452540705400
3388486878919450
3373608476884750
asdfasdf
Is that what you're after?

How to combine results of two queries into a single dataset

I have two queries :
Queries Simplified excluding Joins
Query 1 : select ProductName,NumberofProducts (in inventory) from Table1.....;
Query 2 : select ProductName, NumberofProductssold from Table2......;
I would like to know how I can get an output as :
ProductName NumberofProducts(in inventory) ProductName NumberofProductsSold
The relationships used for getting the outputs for each query are different.
I need the output this way for my SSRS report .
(I tried the union statement but it doesnt work for the output I want to see. )
Here is an example that does a union between two completely unrelated tables: the Student and the Products table. It generates an output that is 4 columns:
select
FirstName as Column1,
LastName as Column2,
email as Column3,
null as Column4
from
Student
union
select
ProductName as Column1,
QuantityPerUnit as Column2,
null as Column3,
UnitsInStock as Column4
from
Products
Obviously you'll tweak this for your own environment...
I think you are after something like this; (Using row_number() with CTE and performing a FULL OUTER JOIN )
Fiddle example
;with t1 as (
select col1,col2, row_number() over (order by col1) rn
from table1
),
t2 as (
select col3,col4, row_number() over (order by col3) rn
from table2
)
select col1,col2,col3,col4
from t1 full outer join t2 on t1.rn = t2.rn
Tables and data :
create table table1 (col1 int, col2 int)
create table table2 (col3 int, col4 int)
insert into table1 values
(1,2),(3,4)
insert into table2 values
(10,11),(30,40),(50,60)
Results :
| COL1 | COL2 | COL3 | COL4 |
---------------------------------
| 1 | 2 | 10 | 11 |
| 3 | 4 | 30 | 40 |
| (null) | (null) | 50 | 60 |
How about,
select
col1,
col2,
null col3,
null col4
from Table1
union all
select
null col1,
null col2,
col4 col3,
col5 col4
from Table2;
The problem is that unless your tables are related you can't determine how to join them, so you'd have to arbitrarily join them, resulting in a cartesian product:
select Table1.col1, Table1.col2, Table2.col3, Table2.col4
from Table1
cross join Table2
If you had, for example, the following data:
col1 col2
a 1
b 2
col3 col4
y 98
z 99
You would end up with the following:
col1 col2 col3 col4
a 1 y 98
a 1 z 99
b 2 y 98
b 2 z 99
Is this what you're looking for? If not, and you have some means of relating the tables, then you'd need to include that in joining the two tables together, e.g.:
select Table1.col1, Table1.col2, Table2.col3, Table2.col4
from Table1
inner join Table2
on Table1.JoiningField = Table2.JoiningField
That would pull things together for you into however the data is related, giving you your result.
If you mean that both ProductName fields are to have the same value, then:
SELECT a.ProductName,a.NumberofProducts,b.ProductName,b.NumberofProductsSold FROM Table1 a, Table2 b WHERE a.ProductName=b.ProductName;
Or, if you want the ProductName column to be displayed only once,
SELECT a.ProductName,a.NumberofProducts,b.NumberofProductsSold FROM Table1 a, Table2 b WHERE a.ProductName=b.ProductName;
Otherwise,if any row of Table1 can be associated with any row from Table2 (even though I really wonder why anyone'd want to do that), you could give this a look.
Old question, but where others use JOIN to combine unrelated queries to rows in one table, this is my solution to combine unrelated queries to one row, e.g:
select
(select count(*) c from v$session where program = 'w3wp.exe') w3wp,
(select count(*) c from v$session) total,
sysdate
from dual;
which gives the following one-row output:
W3WP TOTAL SYSDATE
----- ----- -------------------
14 290 2020/02/18 10:45:07
(which tells me that our web server currently uses 14 Oracle sessions out of the total of 290 sessions; I log this output without headers in an sqlplus script that runs every so many minutes)
Load each query into a datatable:
http://www.dotnetcurry.com/ShowArticle.aspx?ID=143
load both datatables into the dataset:
http://msdn.microsoft.com/en-us/library/aeskbwf7%28v=vs.80%29.aspx
This is what you can do. Assuming that your ProductName column have common values.
SELECT
Table1.ProductName,
Table1.NumberofProducts,
Table2.ProductName,
Table2.NumberofProductssold
FROM Table1
INNER JOIN Table2
ON Table1.ProductName= Table2.ProductName
Try this:
SELECT ProductName,NumberofProducts ,NumberofProductssold
FROM table1
JOIN table2
ON table1.ProductName = table2.ProductName
Try this:
GET THE RECORD FOR CURRENT_MONTH, LAST_MONTH AND ALL_TIME AND MERGE THEM INTO SINGLE ARRAY
$analyticsData = $this->user->getMemberInfoCurrentMonth($userId);
$analyticsData1 = $this->user->getMemberInfoLastMonth($userId);
$analyticsData2 = $this->user->getMemberInfAllTime($userId);
foreach ($analyticsData2 as $arr) {
foreach ($analyticsData1 as $arr1) {
if ($arr->fullname == $arr1->fullname) {
$arr->last_send_count = $arr1->last_send_count;
break;
}else{
$arr->last_send_count = 0;
}
}
foreach ($analyticsData as $arr2) {
if ($arr->fullname == $arr2->fullname) {
$arr->current_send_count = $arr2->current_send_count;
break;
}else{
$arr->current_send_count = 0;
}
}
}
echo "<pre>";
print_r($analyticsData2);die;

Decomposing a GROUP BY statement

Assuming I have a table SomeTable with the following data:
Primary Key Column1 Column2 Column3 Column4 Column5 Num
1 dat1 abc1 dat3 dat4 por7 1
2 dat1 gcd4 dat3 dat4 yrt8 6
3 dat1 iut7 dat3 dat4 asd6 2
4 other1 other2 other3 other4 other5 4
Another table SomeTableGrouped with a "Group Byed" version created using a query like this:
INSERT INTO SomeTableGrouped
SELECT Column1, Column3, Column4, SUM(Num)
FROM SomeTable
GROUP BY Column1, Column3, Column4
Primary Key Column1 Column3 Column4 Num
100 dat1 dat3 dat4 9
200 other1 other3 other4 4
What I'd like to be able to do is, if I have a primary key of SomeTableGrouped, I need to be able to tell which specific rows from SomeTable it came from.
Example:
In a separate table RandomTable, I have data like this:
Primary Key Column1 SomeTableGroupedId
1 dat1 100
2 dat2 100
If I look at the first row, I need to be able to list out row 1 - 3 from SomeTable
How can I do this? I can't change the schema very much (ie. I can only add new columns, remove columns, add a new table) so a dirty solution is perfectly fine with me.
I think this is what you want.
SELECT id
FROM SomeTable
INNER JOIN SomeTableGrouped ON
(SomeTable.Column1 = SomeTableGrouped.Column1) AND
(SomeTable.Column2 = SomeTableGrouped.Column2) AND
(SomeTable.Column3 = SomeTableGrouped.Column3)
WHERE SomeTableGrouped.id = ...
You don't even need to create all those tables, you only need SomeTable. But here we go...
If you want to find the IDs of the records that summed up, just relate them as they were created:
select st.PrimaryKey as STPK, stg.PrimaryKey as STGPK
from SomeTable st
inner join SomeTableGrouped stg
on (st.Column1 = stg.Column1 and
st.Column3 = stg.Column3 and
st.Column5 = stg.Column5)
However, you should not even have created SomeTableGroupedas a table. It could be a view (look here to see how create views in DB2).
That way, you make sure data is always up-to-date and you don't have to worry about back tracking ("what if Num gets updated?").

Merge two tables with same column names, add counters

I have two tables with the same columns, the first column is the name and the second is a count. I would like to merge these tables, so that each name appears with the added count of the two tables:
Table1: Table2: Result Table:
NAME COUNT NAME COUNT NAME COUNT
name1 1 name3 3 name1 1
name2 2 name4 4 name2 2
name3 3 name5 5 name3 6
name4 4 name6 6 name4 8
name5 5
name6 6
As of the moment I have created a pretty ugly structure to execute this, and would like to know if it is possible to get the results in a more elegant way.
What I have so far (Table1 is test1 and Table2 is test2):
create table test1 ( name varchar(40), count integer);
create table test2 ( name varchar(40), count integer);
create table test3 ( name varchar(40), count integer);
create table test4 ( name varchar(40), count integer);
create table test5 ( name varchar(40), count integer);
insert into test4 (name, count) select * from test1;
insert into test4 (name, count) select * from test2;
insert into test3 (name , count) select t1.name, t1.count + t2.count
from test1 t1 inner join test2 t2 on t1.name = t2.name;
select merge_db(name, count) from test3;
insert into test5 (name, count) (select name, max(count) from test4 group by name);
CREATE FUNCTION merge_db(key varchar(40), data integer) RETURNS VOID AS
$$ -- souce: http://stackoverflow.com/questions/1109061/insert-on-duplicate-update-postgresql
BEGIN
LOOP
-- first try to update the key
UPDATE test4 SET count = data WHERE name = key;
IF found THEN
RETURN;
END IF;-- not there, so try to insert the key -- if someone else inserts the same key concurrently, -- we could get a unique-key failure
BEGIN
INSERT INTO test4(name,count) VALUES (key, data);
RETURN;
EXCEPTION WHEN unique_violation THEN-- do nothing, and loop to try the UPDATE again
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
=> create table t1 (name text,cnt int);
=> create table t2 (name text,cnt int);
=> insert into t1 values ('name1',1), ('name2',2), ('name3',3), ('name4',4);
=> insert into t2 values ('name3',3), ('name4',4), ('name5',5), ('name6',6);
=>
select name,sum(cnt) from
(select * from t1
union all
select * from t2 ) X
group by name
order by 1;
name | sum
-------+-----
name1 | 1
name2 | 2
name3 | 6
name4 | 8
name5 | 5
name6 | 6
(6 rows)
How about this, in pure SQL:
SELECT
COALESCE(t1.name, t2.name),
COALESCE(t1.count, 0) + COALESCE(t2.count, 0) AS count
FROM t1 FULL OUTER JOIN t2 ON t1.name=t2.name;
Basically we're doing a full outer join on the name field to merge the two tables. The tricky part is that with the full outer join, rows that exist in one table but not the other will appear, but will have NULL in the other table; so if t1 has "name1" but t2 doesn't, the join will give us NULLs for t2.name and t2.name.
The COALESCE function returns the first non-NULL argument, so we use it to "convert" the NULL counts to 0 and to pick the name from the correct table. Thanks for the tip on this Wayne!
Good luck!
An alternative method is to use the NATURAL FULL OUTER JOIN combined with SUM(count) and GROUP BY name statements. The following SQL code exactly yields the desired result:
SELECT name, SUM(count) AS count FROM
( SELECT 1 AS tableid, * FROM t1 ) AS table1
NATURAL FULL OUTER JOIN
( SELECT 2 AS tableid, * FROM t2 ) AS table2
GROUP BY name ORDER BY name
The artificial tableid column ensures that the NATURAL FULL OUTER JOIN creates a separate row for each row in t1 and for each row in t2. In other words, the rows "name3, 3" and "name4, 4" appear twice in the intermediate result. In order to merge these duplicate rows and to sum the counts we can group the rows by the name column and sum the count column.