use case when with criteria from other table without relational key - sql

I have table A as seen below:
Name Amount
Bob 245.000.000
Jack 98.123.000
Mike 450.000.000
Smith 455.000.000
John 500.000.000
and table B as parameter table as
Class Range_min Range Max
<= 100 MIO 0 100000000
> 100 - 250 MIO 100000001 250000000
> 250 - 450 MIO 250000001 450000000
> 450 MIO 450000001
can somebody help me to get a result as below:
Class #ofRecord
<= 100 MIO 1
> 100 - 250 MIO 1
> 450 MIO 3
and If i modified the parameter table (the class or range), i don't need to change the query.

You can do this with the following statement:
SELECT [Class], COUNT(*)
FROM TableA INNER JOIN TableB
ON TableA.Amount BETWEEN COALESCE(TableB.[Range_min], TableA.Amount) AND
COALESCE(TableB.[Range Max], TableA.Amount)
GROUP BY [Class]
And you can see the working SQLFiddle as well.
Few notes:
1. You have an incorrect "requested output". "Mike" is in 250-450.
2. You really should use some naming convention to your columns. (Range_min, Range Max)

This is a simple join between two tables:
declare #A table (Name varchar(17) not null,Amount bigint not null)
declare #B table (Class varchar(31) not null,Range_min bigint not null,Range_Max bigint null)
insert into #A(Name,Amount) values
('Bob' ,245000000),
('Jack' ,98123000 ),
('Mike' ,450000000),
('Smith',455000000),
('John' ,500000000)
insert into #B(Class,Range_min,Range_Max) values
('<= 100 MIO' ,0 ,100000000),
('> 100 - 250 MIO',100000001,250000000),
('> 250 - 450 MIO',250000001,450000000),
('> 450 MIO' ,450000001,null )
select b.Class,COUNT(*)
from
#B b
inner join
#A a
on
b.Range_min <= a.Amount and
(
a.Amount <= b.Range_Max or
b.Range_Max is null
)
group by b.Class
Which produces the result:
Class
------------------------------- -----------
<= 100 MIO 1
> 100 - 250 MIO 1
> 250 - 450 MIO 1
> 450 MIO 2
Which doesn't match your expected result but does seem correct given the sample data.

Related

SQL join query for a view with sum of columns across 3 tables

I have 3 tables as below
Table - travel_requests
id industry_id travel_cost stay_cost other_cost
1 2 1000 500 200
2 4 4000 100 200
3 5 3000 0 400
4 1 3000 250 100
5 1 200 100 75
Table - industry_tech_region
id industry_name
1 Auto
2 Aero
3 Machinery
4 Education
5 MTV
Table - industry_allocation
id industry_id allocation
1 1 500000
2 2 300000
3 3 500000
4 4 300000
5 5 500000
6 1 200000
I want to create a view which has 3 columns
industry_name, total_costs, total_allocation
I created a view as below
SELECT industry_tech_region.industry_name,
SUM(travel_requests.travel_cost + travel_requests.stay_cost + travel_requests.other_cost) AS total_cost,
SUM(industry_allocation.allocation) AS total_allocation
FROM industry_tech_region
INNER JOIN industry_allocation
ON industry_tech_region.id = industry_allocation.industry_id
INNER JOIN travel_requests
ON industry_tech_region.id = travel_requests.industry_id
GROUP BY industry_tech_region.industry_name
But the result I get is as below which is incorrect
industry_name total_cost total_allocation
Aero 1700 300000
Auto 7450 1400000 (wrong should be 3725 and 700000)
Education 4300 300000
MTV 3400 500000
This is probably happening because there are 2 entries for industry_id 1 in the travel_requests table. But they should be counted only once.
Please let me know how do we correct the view statement.
Also I want to add another column in view which is remaining_allocation which is difference of total_allocation and total_cost for each industry.
you shoud join the sum (and not sum the join)
select
a.industry_name
, t1.total_cost
, t2.total_allocation
from dbo.industry_tech_region a
left join (
select dbo.travel_requests.industry_id
, SUM(dbo.travel_requests.travel_cost + dbo.travel_requests.stay_cost + dbo.travel_requests.other_cost) AS total_cost
FROM bo.travel_requests
group by dbo.travel_requests.industry_id
) t1 on a.id = t1.industry_id
left join (
select dbo.industry_allocation.industry_id
, SUM(dbo.industry_allocation.allocation) AS total_allocation
from dbo.industry_allocation
group by dbo.industry_allocation.industry_id
) t2 on a.id = t2.industry_id
this happen because you have two entry for the industry_id 1 and then the row are joined two time if you use the subquery for aggreated the row this can't happen ...
I have used left join because seems that not all the industry_id match for the 3 tables ..
You can use this approach too (without the ORDER BY because views do not allow it).
;WITH q AS (
SELECT industry_id
, sum(allocation) AS total_allocation
FROM #industry_allocation
GROUP BY industry_id
)
SELECT #industry_tech_region.industry_name
, isnull(SUM(#travel_request.travel_cost
+ #travel_request.stay_cost
+ #travel_request.other_cost),0.0) AS total_cost
,q.total_allocation AS total_allocation
FROM #industry_tech_region
LEFT JOIN q ON #industry_tech_region.id = q.industry_id
LEFT JOIN #travel_request ON #industry_tech_region.id = #travel_request.industry_id
GROUP BY #industry_tech_region.industry_name,q.total_allocation
ORDER BY industry_name

Looping with SQL statements

I am trying to implement a small logic in SQL:
For example : I have two tables A and B
A B
ID Qnt ID Qnt Value
1 50 1 100 1000
2 130 2 200 1000
3 180 3 300 1000
4 320 4 400 2000
5 500 5 500 2000
6 600 2000
7 700 2000
I would to loop through each value of Qnt in TABLE A and check if the value lie between the range of the values in Qnt of TABLE B and get the corresponding value.
I know how I could achieve this with using While loop. But I don't want to do this since looping affects my query performance significantly. I would like to do this with only SQL statements.
Can anyone suggest an idea how I could go with this? just an idea would be great! Any sql would be fine, I would like to know just the logic.
The output would look like :
Output
ID Qnt Value
1 50 1000
2 130 1000
3 180 1000
4 320 2000
5 500 2000
Thanks
This is a lookup. You can do it with a correlated subquery, although the syntax is a bit different in the two databases. Here is the MySQL version:
select a.*,
(select b.value
from b
where b.qnt <= a.qnt
order by b.qnt desc
limit 1
) as value
from a;
Here is the SQL Server version:
select a.*,
(select top 1 b.value
from b
where b.qnt <= a.qnt
order by b.qnt desc
) as value
from a;

Select Previous Record in SQL Server 2008

Here's the case: I have one table myTable which contains 3 columns:
ID int, identity
Group varchar(2), not null
value decimal(18,0), not null
Table looks like this:
ID GROUP VALUE Prev_Value Result
------------------------------------------
1 A 20 0 20
2 A 30 20 10
3 A 35 30 5
4 B 100 0 100
5 B 150 100 50
6 B 300 200 100
7 C 40 0 40
8 C 60 40 20
9 A 50 35 15
10 A 70 50 20
Prev_Value and Result columns should be custom columns. I need to make it on view. Anyone can help? please... Thank you so much.
The gist of what you need to do here is to join the table to itself, where part of the join condition is that the value column of the joined copy of the table is less than value column of the original. Then you can group by the columns from the original table and select the max value from the joined table to get your results:
SELECT t1.id, t1.[Group], t1.Value
, coalesce(MAX(t2.Value),0) As Prev_Value
, t1.Value - coalesce(MAX(t2.Value),0) As Result
FROM MyTable t1
LEFT JOIN MyTable t2 ON t2.[Group] = t1.[Group] and t2.Value < t1.Value
GROUP BY t1.id, t1.[Group], t1.Value
Once you can update to Sql Server 2012 you'll also be able to take advantage of the new LAG keyword.

How to insert multiple new records based on record(s) already in the table (in Oracle)?

I want to find rows (records) which have a specific vlaue (S) in a column (Type), and insert multiple rows (e.g. 2) based on that row in the same table.
For example, in tabe t1 below, I want for each row of type 'S', 2 rows be inserted with same ID and Price, new Counter value (no specific requirement for this filed, however the Counter for records with same ID must be different), and Type will be 'B'.
It means that when inserting 2 rows based on the first record in table below (1,1200,S,200), Counter value of the new records must be different from Counter values of the records with ID=1 already in the table (1200 and 1201). So, in the initial table there were three records with Type 'S', then in the final table, for each of those records, 2 new records with Type 'B' and a new Counter value is insrted:
ID Counter Type Price
------------------------
1 1200 S 200
1 1201 T 400
2 1200 T 500
3 1546 S 100
3 1547 S 70
4 2607 M 250
The output table t1 will be:
ID Counter Type Price
------------------------
1 1200 S 200
1 1202 B 200
1 1203 B 200
1 1201 T 400
2 1200 T 500
3 1546 S 100
3 1548 B 100
3 1549 B 100
3 1547 S 700
3 1550 B 700
3 1552 B 700
4 2607 M 250
You just have to play twice this command:
insert into epn
with w(max) as
(
select max(t.counter) from t -- useful to get max counter value
)
select t.id, w.max + rownum, 'B', t.price -- max + rownum then gives new values
from t, w
where t.type != 'B'; -- Avoid duplicating rows added 1st time
This gives:
1 1 1200 S 200
2 1 2617 B 200
3 1 2611 B 200
4 1 1201 T 400
5 1 2618 B 400
6 1 2612 B 400
7 2 1200 T 500
8 2 2613 B 500
9 2 2619 B 500
10 3 1547 S 70
11 3 2609 B 70
12 3 2615 B 70
13 3 1546 S 100
14 3 2614 B 100
15 3 2608 B 100
16 4 2607 M 250
17 4 2610 B 250
18 4 2616 B 250
You need an insert select statement:
insert into t1 (ID, Counter, Type, Price)
select ID, Counter+1, 'B', Price from t1 where Type = 'S'
union all
select ID, Counter+2, 'B', Price from t1 where Type = 'S';
EDIT: Here is a statement that matches your criteria mentioned in your remark below. It gets the maximum Counter per ID and adds the count # of the added entry to the ID (1, 2, 3, ...) to it.
insert into t1 (ID, Counter, Type, Price)
select
ID,
(select max(Counter) from t1 where ID = src.ID) + row_number() over (partition by ID order by Price) as new_counter,
'B' as Type,
Price
from
(
select ID, Price
from t1
join (select * from dual union all select * from dual) twice
where t1.Type = 'S'
) src;
CREATE SEQUENCE my_SEQ
INCREMENT BY 1
START WITH 1
MINVALUE 1
MAXVALUE 999999999
NOCYCLE
NOCACHE
NOORDER;
create table MY_TABLE1
( ID NUMBER,
counter number(20),
type varchar2(30),
price number
)
insert into my_table1 (id,counter,type,price)
values (my_SEQ.nextval,1200,'S',200);
insert into my_table1 (id,counter,type,price)
values (my_SEQ.nextval,1300,'B',311);
insert into my_table1 (id,counter,type,price)
values (my_SEQ.nextval,200,'S',110);
insert into my_table1 (id,counter,type,price)
values (my_SEQ.nextval,299,'B',329);
select * from my_table1
ID COUNTER TYPE PRICE
62 1200 S 200
63 1300 B 311
64 200 S 110
65 299 B 329
declare
cursor c1 is select * from My_table1 where type='B';
begin
for rec IN c1
loop
insert into my_table1 (id,counter,type,price)
values (my_SEQ.nextval,rec.counter+1,'Z',rec.price);
end loop;
end;
select * from my_table1
ID COUNTER TYPE PRICE
63 1300 B 311
65 299 B 329
64 200 S 110
62 1200 S 200
66 1301 Z 311
67 300 Z 329
6 rows selected.
So there in cursor select all rows where type is ='B' and insert them back with changed values a little! hope this helps. you could not use sequence but add rec.id+1

MS SQL Update Query - Update all rows with that have the same field A to the top A value

this should be easy, but I'm just not getting it. I have a temp table that I'm populating from different sources. In the temp table, I have a bunch of rows that might have the same subdivision name. If the subdivision name is the same, then I want the top latitude and longitude for that subdivision to be in all rows for that subdivision. I need to do it for all the subdivisions in the temp table that have an iSource = to 1 or 2. The iSource field is the different sources. 0 is the most reliable, 4th there is not lat or long.
Temp Table
SubdivisionName Latitude Longitude iSource
A 100 200 0
A 100 200 0
A 102 200 2
B 104 202 1
B 105 203 1
B 106 202 2
Desired Result
SubdivisionName Latitude Longitude iSource
A 100 200 0
A 100 200 0
A 100 200 2
B 104 202 1
B 104 202 1
B 104 202 2
I tried this, but SQL Server 2005 doesn't like my prefixes TT or TTT. Can you point me in the right direction?
WITH #TempTable
AS
(
SELECT TTT.*
, ROW_NUMBER() OVER(PARTITION by SubdivisionName ORDER BY iSource) AS rnk
from #TempTable TTT WHERE iSource IN (1,2)
)
UPDATE #TempTable
SET FieldSheetLat = TTT.FieldSheetLat, Longitude = TTT.FieldSheetLong
;WITH CTE
AS
(
SELECT TTT.*
, ROW_NUMBER() OVER(PARTITION by SubdivisionName ORDER BY iSource) AS rnk
FROM #TempTable TTT
)
UPDATE TT
SET FieldSheetLat = CTE.FieldSheetLat, Longitude = CTE.FieldSheetLong
FROM #TempTable TT
JOIN CTE ON CTE.SubdivisionName = TT.SubdivisionName
AND CTE.rnk = 1
WHERE TT.iSource > 0;
Notes
The last filter is because there is no never a need to update any iSource = 0 rows
The UPDATE from JOIN pattern is used here, where the source is the CTE where rnk=1 and the target is the #TempTable aliased as TT