order by in UNION clauses existing a view in left join - sql

I need to order the results by one attribute: Series (for each table). There is also a view in the queries...
The original query is:
select zz.Id
,t1.Series
,t1.SeriesType
from table 1 t1
inner join another_table (...)
left join view (...)
UNION
select zz.Id
,t2.Series
,t2.SeriesType
from table 2 t2
inner join another_table (...)
left join view (...)
UNION
select zz.Id
,t3.Series
,t3.SeriesType
from table 3 t3
inner join another_table (...)
left join view (...)
order zz.Id
this works, but only gives the order for zz.Id. How to achieve order for the Series column for each table?
I have tried with:
select *
from
(
SELECT TOP 100 PERCENT zz.Id
,t1.Series
,t1.SeriesType
from table 1 t1
inner join another_table (...)
left join view (...)
order by t1.Series) as table1
UNION
select *
from
(
SELECT TOP 100 PERCENT zz.Id
,t2.Series
,t2.SeriesType
from table 2 t2
inner join another_table (...)
left join view (...)
order by t2.Series) as table2
UNION
select *
from
(
SELECT TOP 100 PERCENT zz.Id
,t3.Series
,t3.SeriesType
from table 3 t3
inner join another_table (...)
left join view (...)
order by t3.Series) as table3
.
all tables are connected by one single table and this zz.id is unique for table1, table2, table3. (I see it is not possible to sort out at same time Id and series. so I will only sort out the series by each type of series). Thanks.
for example table 1 is:
zz.Id t1.series SERIES_Type
---------------------------------
1 4545 1
2 5655 1
3 2344 1
table 2
zz.Id t2.series SERIES_Type
---------------------------------
4 4546 2
table 3
zz.Id t3.series SERIES_Type
--------------------------------
5 545 3
6 343 3
7 2344 3
final result should be:
zz.Id series SERIES_Type
--------------------------------
3 2344 1
1 4545 1
2 5655 1
4 4546 2
6 343 3
5 545 3
7 2344 3

Have you tried two keys in the order by?
order Id, SeriestType

Based on your "expected result" all you need is:
order by SeriesType, Series
Am I correct or misunderstood...

Whenever it occurs to you to use "top 100 percent" - stop coding. This does nothing useful EVER. A resultset (which is the only way to look at rows) has no reliable or defined order unless the query that generated it included an order by clause. If you want to order rows according to each query within the union, then you need to add a column to each query that you can include in the final sort by clause.
As an example, this adds a column named srt (because "sort" is a reserved word - pick a name meaningful to you). Each participating query in the union is assigned a value that can be used to identify the "group". Again - make it meaningful to you. That column is included in the final sort by clause as the first column and will "group" rows.
use tempdb;
go
declare #tbl table (id int not null, series varchar(10) not null, seriestype varchar(10) not null);
insert #tbl(id, series, seriestype) values (1, '4521', 1), (1, '3011', 1), (2, '9999', 2), (3, '0000', 1), (3, '1111', 1);
select id, series, seriestype, 1 as srt from #tbl where id = 1
union all
select id, series, seriestype, 2 as srt from #tbl where id = 2
union all
select id, series, seriestype, 3 as srt from #tbl where id = 3
order by srt, id, series;
Now that you have updated your post, it seems you might be able to sort by seriestype. But that might be accidental due to your query logic and the data that exists in your tables. Only you can know if that is correct. TBH, I find the use of a union (vis-a-vis your sample output) suspicious.
And one final note. If you do not want to see the srt column in the final resultset, that can be arranged.

Related

SQL statement to combine two tables and show delta values in one row

I'm trying to match 2 tables using a SQL statement where attributes form one column are combined and the other values (delta) will be shown in the same row. For example the following 2 tables are given (which already result from some filtered and counted SQL querys):
Table 1:
Name
Count
First
4
Second
7
Third
2
Fourth
12
Fith
5
Table 2:
Name
Count
First
13
Second
6
Third
2
Fourth
12
Sixth
3
Expected result:
Name
Count table 1
Count table 2
First
4
13
Second
7
6
Third
2
2
Fourth
12
12
Fith
5
0
Sixth
0
3
Tried to use UNION and googled but don't get the needed result.
Should just be a simple join:
select table1.Name, table1.Count, table2.Count
from table1
join table2 on table1.Name = table2.Name
You need to use an inner join statement.
"SELECT [Table 1].Name, [Table 1].Count, [Table 2].count FROM [Table 1] INNER JOIN [Table 2] ON [Tabel 1].Name = [Table 2].Name"
You just try this one.
Create TABLE #A
(
name_a varchar(50),
column_a int
)
Create TABLE #B
(
name_b varchar(50),
column_b int
)
Insert INTO #A(name_a,column_a) values('First',4)
Insert INTO #A(name_a,column_a) values('Second',7)
Insert INTO #A(name_a,column_a) values('Third',2)
Insert INTO #A(name_a,column_a) values('Fourth',12)
Insert INTO #A(name_a,column_a) values('Fith',5)
Insert INTO #B(name_b,column_b) values('First',13)
Insert INTO #B(name_b,column_b) values('Second',6)
Insert INTO #B(name_b,column_b) values('Third',2)
Insert INTO #B(name_b,column_b) values('Fourth',12)
Insert INTO #B(name_b,column_b) values('Sixth',3)
select isnull(#A.name_a,#B.name_b) as Name, isnull(#A.column_a,0) as 'Count table 1', isnull(#B.column_b,0) as 'Count table 2'
from #A
full outer join #B on #A.name_a = #B.name_b
drop table #A
drop table #B
If you want to combine rows so as to get a result row consisting of the two original rows, then you join. In your case one data set may contain a name the other doesn't, so in your case a FULL OUTER JOIN may be appropriate. If it is guaranteed to find each name in both sets or you want to restrict the result to those names occurring in both sets, then INNER JOIN instead.
You say that the tables you are showing are query results. This is no problem, as you can join query results just as you join persisted database tables, because, well, a query result is a table, too.
select
name,
coalesce(q1.cnt, 0) as t1_count,
coalesce(q2.cnt, 0) as t2_count
from (select name, count(*) as cnt from t1 group by name) q1
full outer join (select name, count(*) as cnt from t2 group by name) q2 using (name)
order by name;
Based on your question, because one table can have name values that the other table does not, you could use FULL OUTER JOIN. You could try something like this:
SELECT
COALESCE(table1.Name, table2.Name) AS Name,
COALESCE(table1.Count,0) as "Count table 1",
COALESCE(table2.Count,0) as "Count table 2"
FROM
table1
FULL OUTER JOIN table2 ON table1.Name = table2.Name
;

Exclude one item with different corelated value in the next column SQL

I have two tables:
acc_num
ser_code
1
A
1
B
1
C
2
C
2
D
and the second one is:
ser_code
value
A
5
B
8
C
10
D
15
I want to exclude all the accounts with the service codes that they have value of 10 or 15.
Because my data set is huge, I want to use NOT EXIST but it just excludes combination of acc_num and ser_code.
I want to exclude the acc_num with all of it's ser_code, because on of it's ser_code meats my criteria.
I used:
select acc_num, ser_code
from table 1
where NOT EXIST (select 1
FROM table 2 where acc_num = acc_num and value in (10, 15)
out put with above code is:
acc_num
ser_code
1
A
1
B
Desire out put would be empty.
here you are
select t1.acc_num,t1.ser_code from table1 t1, table2 t2
where (t1.ser_code=t2.ser_code and t2.value not in (10,15))
and t1.acc_num not in
(
select t3.acc_num from table1 t3,table2 t4
where t1.acc_num=t3.acc_num and t3.ser_code=t4.ser_code
and t4.value in (10,15)
) ;
This could be achieved in many ways. However using NOT EXISTS is the best option. The problem with your query is for acc_num 1, there are ser_code that does not have value as 10, 15. So you will get A and B in result.
To overcome that you must pull acc_num inside the sub-query
Query 1 (using NOT EXISTS):
As you can see in the below query, I have included acc_num inside sub-query, so that the filter works properly,
SELECT DISTINCT a.acc_num, a.ser_code
FROM one as a
WHERE NOT EXISTS
(
SELECT DISTINCT one.acc_num
FROM two
INNER JOIN one
ON one.ser_code=two.ser_code
WHERE value IN (10,15) AND a.acc_num=one.acc_num
)
Query 2 (using LEFT JOIN):
NOT EXISTS often confusing due to it's nature (super fast though). Hence LEFT JOIN could also be used (expensive than NOT EXISTS),
SELECT DISTINCT a.acc_num, a.ser_code
FROM one as a
LEFT JOIN
(
SELECT DISTINCT one.acc_num
FROM two
INNER JOIN one
ON one.ser_code=two.ser_code
WHERE value IN (10,15)
) b
ON a.acc_num=b.acc_num
WHERE b.acc_num IS NULL
Query 3 (using NOT IN):
NOT IN would also achieve this with comprehensive query but expensive than both of the above methods,
SELECT DISTINCT a.acc_num, a.ser_code
FROM one as a
WHERE a.acc_num NOT IN
(
SELECT DISTINCT one.acc_num
FROM two
INNER JOIN one
ON one.ser_code=two.ser_code
WHERE value IN (10,15)
)
All 3 would yield same result. I would prefer to go with NOT EXISTS
See demo with time consumption in db<>fiddle

SQL Subquery Join and Sum

I have Table 1 & 2 with common Column name ID in both.
Table 1 has duplicate entries of rows which I was able to trim using:
SELECT DISTINCT
Table 2 has duplicate numeric entries(dollarspent) for ID's which I needed and was able to sum up:
Table 1 Table 2
------------ ------------------
ID spec ID Dol1 Dol2
54 A 54 1 0
54 A 54 2 1
55 B 55 0 2
56 C 55 3 0
-I need to join these two queries into one so I get a resultant JOIN of Table 1 & Table 2 ON column ID, (a) without duplicates in Table 1 & (b) Summed $ values from Table 2
For eg:
NewTable
----------------------------------------
ID Spec Dol1 Dol2
54 A 3 1
55 B 3 2
Notes : No. of rows in Table 1 and 2 are not the same.
Thanks
Use a derived table to get the distinct values from table1 and simply join to table 2 and use aggregation.
The issue you have is you have a M:M relationship between table1 and table2. You need it to be a 1:M for the summations to be accurate. Thus we derive t1 from table1 by using a select distinct to give us the unique records in the 1:M relationship (assuming specs are same for each ID)
SELECT T1.ID, T1.Spec, Sum(T2.Dol1) as Dol1, sum(T2.Dol2) as Dol2
FROM (SELECT distinct ID, spec
FROM table1) T1
INNER JOIN table2 T2
on t2.ID = T1.ID
GROUP BY T1.ID, T1.Spec
This does assume you only want records that exist in both. Otherwise we may need to use an (LEFT, RIGHT, or FULL) outer join; depending on desired results.
I can't really see your data, but you might want to try:
SELECT DISTINCT ID
FROM TblOne
UNION ALL
SELECT DISTINCT ID, SUM(Dol)
FROM TblTwo
GROUP BY ID
Pre-aggregate table 2 and then join:
select t1.id, t1.spec, t2.dol1, t2.dol2
from (select t2.id, sum(dol1) as dol1, sum(dol2) as dol2
from table2 t2
group by t2.id
) t2 join
(select distinct t1.id, t1.spec
from table1 t1
) t1
on t1.id = t2.id;
For your data examples, you don't need to pre-aggregate table 2. This gives the correct sums -- albeit in multiple rows -- if table1 has multiple specs for a given id.

updating a table with a column from another table in SQL

I need to create a new table from data I extract from the following two tables:
First table:
Var cur_number
-------------------
A 10
B 8
Second table:
Var new_number
-------------------
A 2
A 11
B 4
B 6
The new table should contain a 'Var' column and a 'Number' column, where for each variable there will be one line with its cur_number, and the rest of the lines will contain the numbers from second table's new_number columns, where the new_number < cur_number.
For example, in the example shown above, for A there will be one line with 10 (its cur_number) and one line with '2' (since 2<10, however 11>10).
in my example the new table will be:
Var Number
A 10
A 2
B 8
B 4
B 6
The database is very large, and the running time is crucial so I cannot use UNION on the two tables...
This script assumes there is only one record per "Var" value in table1.
--insert all from table1
insert into newtable (var,number)
select var,cur_number from table1 t1
--insert from table2 where new_number < cur_number
insert into newtable (var,number)
select t2.var, t2.new_number
from table2 t2
inner join table1 t1 on t1.var = t2.var and t2.new_number < t1.cur_number
Try this:
SELECT var, cur_number
FROM FirstTable
UNION
SELECT t2.var, t2.new_number
FROM Firsttable t1
INNER JOIN SecondTable t2 ON t2.new_number < t1.cur_number;
Update: If you are using SQL Server 2008 or above, you can use MERGE to merge the two tables into one like so:
MERGE INTO FirstTable AS TGT
USING SecondTable AS SRC
ON SRC.new_number >= TGT.cur_number
WHEN NOT MATCHED THEN
INSERT (var, cur_number)
VALUES (SRC.var, SRC.new_number);
SQL Fiddle Demo
This will merge the two tables values into the first table. The first table will contains:
VAR CUR_NUMBER
A 10
B 8
A 2
B 4
B 6
Note that: When using MERGE:
You have to terminate the MERGE statement with a semicolon, It is mandatory.
You can't INSERT inside WHEN MATCHED thats why I used the reverse condition >= in the WHEN NOT MATCHED.
If you know for a fact that you do not have duplicate values in those 2 tables, then you can use UNION ALL instead of UNION
select * from table1 t1
UNION ALL
select* from table2 t2
where t2.new_number < t1.cur_number
By duplicate values, I mean something like this:
table1:
var cur_number
A 8
table2:
var new_number
A 8
The difference is that UNION ALL is faster than UNION, due to the fact that UNION eliminates duplicates from the resultset by using SELECT DISTINCT. In the case you have duplicates, UNION is the best in my experience.
Why not use Union? * SQLFIDDLE
select * from first
union
select s.var,s.new_number from second s
join first f
on s.var = f.var
join (
select f.var, max(f.cur_number) maxn
from first f
group by f.var) as t
on t.var = s.var
where s.new_number < t.maxn
order by var asc
;
results
VAR CUR_NUMBER
A 10
B 8
A 2
B 4
B 6

How to write a query to get only first matching row while joining two tables?

Consider I have following tables:
Table 1:
AId
AMoniker
Table2:
BId
BMoniker
Table1->Table2 is one to many relationship
I want a temp table out of these two tables where if a particular Amoniker has multiple BMonikers then only first one should go in the table.
For example, if tables have following data:
Table1:
1 ABCD
2 DEFG
3 QWER
Table 2:
1 QZ
1 XC
1 CV
2 DE
2 OP
3 QW
the query should return the following:
ABCD QZ
DEFG DE
QWER QW
My query to get all the rows is:
select b.BMoniker, a.AMoniker
into #moniker_map
from Table1 a inner join Table2 b
on a.Aid=b.Bid
How can i modify this to get only 1st row from Table2 for each Id.
I tried following query:
select b.BMoniker, a.AMoniker
from Table1 a inner join Table2 b
on a.Aid=b.Bid
and b.BMoniker in
(
select top 1 BMoniker
from Table2
where Bid=cb.Bid
ORDER BY BMoniker
)
But i get following error:
Incorrect syntax near keyword 'top'
Sybase error code = 156, SQLState="ZZZZZ"
Selects row with minimum value from table2 (PostgreSQL syntax):
SELECT a.AMoniker, MIN(b.BMoniker) FROM Table1 a, Table2 b
WHERE a.Aid = b.Bid GROUP BY 1;
Maybe it is a typo but it looks like your subquery is referencing an table alias cb which is not defined. Can you try this:
select b.BMoniker, a.AMoniker
from Table1 a inner join Table2 b
on a.Aid=b.Bid
and b.BMoniker in
(
select top 1 BMoniker
from Table2 c
where c.Bid=a.Bid
ORDER BY BMoniker
)