T-SQL: finding rows in a different table without joins - tsql

I have two tables, I'll call TableA and TableB
TableA:
StartNumber EndNumber Country
1 10 USA
11 20 USA
21 30 Canada
31 40 France
41 50 France
51 60 Germany
TableB:
SomeNumber
5
15
55
22
35
46
49
For each number in TableB, I want to find the corresponding row in TableA where the number is between the StartNumber and EndNumber and return the name of the country. I then want to group these results on the country column and return the number of times each country appears. So the results would look like this:
Country Occurrences
USA 2
Germany 1
Canada 1
France 3
Not sure how to do this.

Here the query.
Select A.Country, count(*) as Occurrences
from
tableA A
inner join
tableB B
on B.someNumber between a.startnumber and b.endnumber
group by A.country

This should do the trick (but does use a join):
declare #TableA table (StartNumber int, EndNumber int, Country varchar(16));
insert into #TableA (StartNumber, EndNumber, Country)
select 1, 10, 'USA' union
select 11, 20, 'USA' union
select 21, 30, 'Canada' union
select 31, 40, 'France' union
select 41, 50, 'France' union
select 51, 60, 'Germany';
declare #TableB table (SomeNumber int);
insert into #TableB (SomeNumber)
select 5 union
select 15 union
select 55 union
select 22 union
select 35 union
select 46 union
select 49;
select
a.Country, count(*) Occurrences
from
#TableA a inner join
#TableB b on b.SomeNumber between a.StartNumber and a.EndNumber
group by
a.Country;

Related

Oracle SQL return value from child table with minimum row number with values in specific list

I have a need to select all rows from a table (main table) and join to another table (child table). In the results set, I want to include one column from the child table, that is only the first row / line number with a column value in a specified list. If there is no match for the specified list, it should be (null)
Desired Result:
ORDER_NO
ORDER_DATE
ORDER CUST
ORDER_VALUE
ITEM
1
02/14/2022
12345
$1,000.00
APPLES
2
02/13/2022
67890
$5,000.00
(null)
3
02/12/2022
45678
$100.00
PEARS
Example:
Main Table: Order Table
Order Number (Handle)
Order Date,
Order Customer,
Order Value
ORDER_NO
ORDER_DATE
ORDER CUST
ORDER_VALUE
1
02/14/2022
12345
$1,000.00
2
02/13/2022
67890
$5,000.00
3
02/12/2022
45678
$100.00
Child Table: Order Details Tbl
Order Number (Handle)
Line Number = Order Line No
Ordered Item,
Ordered Qty
ORDER_NO
LINE_NO
ITEM
1
10
APPLES
1
20
ORANGES
1
30
LETTUCE
2
10
BROCCOLI
2
20
CAULIFLOWER
2
30
LETTUCE
3
10
KALE
3
20
RADISHES
3
30
PEARS
In this example, the returned column is essentially the first line of the order that is a fruit, not a vegetable. And if the order includes no matching fruit, null is returned.
What my code is thus far:
SELECT
MAIN.ORDER_NO,
MAIN.ORDER_DATE,
MAIN.ORDER_CUST,
MAIN.ORDER_VALUE,
B.ITEM
FROM
MAIN
LEFT JOIN
(
SELECT
CHILD.ORDER_NO,
CHILD.LINE_NO,
CHILD.ITEM
FROM
CHILD
WHERE
CHILD.ORDER_NO||'_'||LINE_NO IN
(
SELECT
CHILD.ORDER_NO||'_'||MIN(LINE_NO) AS ORDER_LINE_NO
FROM
CHILD
WHERE
CHILD.ITEM IN ('APPLES','ORANGES','PEACHES','PEARS','GRAPES')
GROUP BY
CHILD.ORDER_NO
)
) B ON MAIN.ORDER_NO = B.ORDER_NO
'''
This code is of course not working as desired, as table 'B' is including all results from CHILD.
From Oracle 12, you can use:
SELECT o.*,
d.item
FROM orders o
LEFT OUTER JOIN LATERAL(
SELECT *
FROM order_details d
WHERE o.order_no = d.order_no
AND item IN ('APPLES','ORANGES','PEACHES','PEARS','GRAPES')
ORDER BY line_no ASC
FETCH FIRST ROW ONLY
) d
ON (1 = 1)
In earlier versions you can use:
SELECT o.*,
d.item
FROM orders o
LEFT OUTER JOIN(
SELECT d.*,
ROW_NUMBER() OVER (PARTITION BY order_no ORDER BY line_no ASC)
AS rn
FROM order_details d
WHERE item IN ('APPLES','ORANGES','PEACHES','PEARS','GRAPES')
) d
ON (o.order_no = d.order_no AND rn = 1)
Which, for the sample data:
CREATE TABLE orders (ORDER_NO, ORDER_DATE, ORDER_CUST, ORDER_VALUE) AS
SELECT 1, DATE '2022-02-14', 12345, 1000.00 FROM DUAL UNION ALL
SELECT 2, DATE '2022-02-13', 67890, 5000.00 FROM DUAL UNION ALL
SELECT 3, DATE '2022-02-12', 45678, 100.00 FROM DUAL;
CREATE TABLE Order_Details (ORDER_NO, LINE_NO, ITEM) AS
SELECT 1, 10, 'APPLES' FROM DUAL UNION ALL
SELECT 1, 20, 'ORANGES' FROM DUAL UNION ALL
SELECT 1, 30, 'LETTUCE' FROM DUAL UNION ALL
SELECT 2, 10, 'BROCCOLI' FROM DUAL UNION ALL
SELECT 2, 20, 'CAULIFLOWER' FROM DUAL UNION ALL
SELECT 2, 30, 'LETTUCE' FROM DUAL UNION ALL
SELECT 3, 10, 'KALE' FROM DUAL UNION ALL
SELECT 3, 20, 'RADISHES' FROM DUAL UNION ALL
SELECT 3, 30, 'PEARS' FROM DUAL;
Both output:
ORDER_NO
ORDER_DATE
ORDER_CUST
ORDER_VALUE
ITEM
1
2022-02-14 00:00:00
12345
1000
APPLES
2
2022-02-13 00:00:00
67890
5000
null
3
2022-02-12 00:00:00
45678
100
PEARS
db<>fiddle here

How I can find duplicate values in the result of a join operation?

I have two tables
MappingTable > Id, ItemId, Quantity
ItemTable > ItemId, Name, DateOfPurchase
I wanted to find out the duplicate rows having same Quantity and same DateOfPurchase.
eg. I have
Id ItemId Quantity
1 01 4
2 03 5
3 05 4
ItemId Name DateOfPurchase
01 AB 2019-10-30 18:30:00
05 XY 2019-10-30 18:17:00
Result:
Quantity DateOfPurchase Name
4 2019-10-30 AB
4 2019-10-30 XY
So, I might join these tables and then find duplicates
How can I do that?
One option is to use window funtions, if your database supports them:
select *
from (
select
m.*,
i.name,
i.dateOfPurchase,
count(*) over(partition by m.quantity, p.dateOfPurchase) cnt
from mapping m
inner join item i on i.itemId = m.itemId
) t
where cnt > 1
order by quantity, dateOfPurchase

results mismatched when retrieved dates from column of type character varying

I have two tables,i want to get the min and max date stored in table1 cfrange column which is of type character varying.
table1 and table2 is mapped using sid. i want to get the max and min date range when compared with sid of table2.
table1:
sid cfrange
100 3390
101 8000
102 5/11/2010
103 11/12/2016
104 01/03/2016
105 4000
106 4000
107 03/12/2017
108 03/11/2016
109 4/04/2018
110 10/12/2016
table2:
sid description
102 success
103 success
104 Proceeding
107 success
108 success
I tried as below but its not giving the correct min and max value.Please advice.
select max(t1.cfrange),min(t1.cfrange) from table1 t1,table2 t2 where t1.sid=t2.sid;
You should join two tables and cast cfrange as a date and cross your fingers. (May be you must format it as a date before to cast it).
create table table1 (sid int, cfrange varchar(30));
insert into table1 values
(100, '3390'),
(101, '8000'),
(102, '5/11/2010'),
(103, '11/12/2016'),
(104, '01/03/2016'),
(105, '4000'),
(106, '4000'),
(107, '03/12/2017'),
(108, '03/11/2016'),
(109, '4/04/2018'),
(110, '10/12/2016');
create table table2 (sid int, description varchar(30));
insert into table2 values
(102, 'success'),
(103, 'success'),
(104, 'Proceeding'),
(107, 'success'),
(108, 'success');
select 'Min' as caption, min(cfrange) as value
from (select table1.sid, table1.cfrange::date
from table1
inner join table2
on table1.sid = table2.sid) tt
UNION ALL
select 'Max' as caption, max(cfrange) as value
from (select table1.sid, table1.cfrange::date
from table1
inner join table2
on table1.sid = table2.sid) tt;
caption | value
:------ | :---------
Min | 2010-11-05
Max | 2017-12-03
dbfiddle here

generate new column using other columns

I am stuck with generating a new column. The table has three columns(C_ID, C_rank, Date).
C_ID C_ Rank NewColumn(Cycle) Date
42 A 1 October 14, 2010
42 B 1 October 26, 2010
42 A 2 February 16, 2011
43 A 1 December 17, 2010
44 A 1 July 28, 2010
44 B 1 August 10, 2010
44 A 2 January 11, 2011
44 B 2 January 28, 2011
45 A 1 July 30, 2010
45 B 1 August 9, 2010
45 B 1 September 24, 2010
45 A 2 April 5, 2011
45 B 2 April 26, 2011
I want to generate one more column called Cycle in such a way that for each C_ID, it should generate the number start from one and increment the number from next C_rank = 'A' (a shown above).
I tried using row_number, but no luck.
Maybe some loop option till next C_Rank = 'A' works.
How can this be done?
You should be able to get this done using ROW_NUMBER() and PARTITION BY
;WITH YourDataCTE AS
(
SELECT
C_ID, C_Rank, Date,
ROW_NUMBER() OVER(PARTITION BY C_ID,C_Rank ORDER BY Date DESC) AS 'Cycle'
FROM
dbo.YourTable
)
SELECT *
FROM YourDataCTE
Does that do what you're looking for??
The PARTITION BY C_ID,C_Rank will cause the ROW_NUMBER to start at 1 again for each different value of C_ID,C_Rank - I didn't know what ORDER BY clause within a single partition (a single value of C_ID,C_Rank) you're looking for and just guessed it might be Date DESC (newest date first).
You could count the number of previous A's in a subquery:
select *
, (
select count(*)
from #YourTable yt2
where yt2.C_ID = yt1.C_ID
and yt2.C_Rank = 'A'
and yt2.Date <= yt1.Date
) as Cycle
from #YourTable yt1
order by
C_ID, Date
Example at ODATA.
Do a self join for all records with the same C_ID, a previous date, and a C_Rank='A' and count them.
select t1.C_ID, t1.C_Rank, count(t2.C_Rank) Cycle, t1.Date
from MyTable t1
left join MyTable t2 on t1.C_ID=t2.C_ID
and t2.Date<=t1.Date
and t2.C_Rank='A'
group by t1.C_ID, t1.C_Rank, t1.Date
order by t1.C_ID, t1.Date
Below code fulfill the requirement:
create table #Temp_Table
(
C_ID int
, C_Rank char(1)
, Date datetime
, NewColumn int
)
insert into #Temp_Table
(
C_ID
, C_Rank
, Date
)
select 42, ‘A’, ’10/14/2010′
union all
select 42, ‘B’, ’10/26/2010′
union all
select 42, ‘B’, ’10/14/2010′
union all
select 42, ‘C’, ’10/26/2010′
union all
select 42, ‘A’,’02/16/2011′
union all
select 43, ‘A’, ’12/17/2010′
union all
select 44, ‘A’, ’07/28/2010′
union all
select 44, ‘B’, ’08/10/2010′
union all
select 44, ‘A’, ’01/11/2011′
union all
select 44, ‘B’, ’01/28/2011′
union all
select 44, ‘C’, ’10/14/2010′
union all
select 44, ‘D’, ’10/26/2010′
Select ‘Original Data’ Comment
,*
from #Temp_Table
/*
This would be Actual Script to get the New ID based on information you provided
*/
Declare #Count int
,#C_ID int
,#C_Rank char(1)
,#total_Count int
,#Count_Partition int
,#Previous_ID int
Declare #Table Table (ID int IDENTITY(1,1), C_ID int, C_Rank char(1), Date datetime, NewColumn int )
Set #Count = 1
Set #Count_Partition = 0
insert into #Table
Select *
from #Temp_Table
Select #total_Count = ISNULL(MAX(ID),0)
from #Table
While #Count < = #total_Count
Begin
Select #C_ID = C_ID
,#C_Rank = C_Rank
From #Table
Where ID = #Count
If #Count = 1
Set #Previous_ID = #C_ID
If #Previous_ID != #C_ID
Set #Count_Partition = 1
Else If #C_Rank = 'A'
Set #Count_Partition = #Count_Partition + 1
update #Table
Set NewColumn = #Count_Partition
Where ID = #Count
Set #Previous_ID = #C_ID
Set #Count = #Count + 1
End
Select C_ID
, C_Rank
, [Date]
, NewColumn
from #Table
–Drop table #Temp_Table

How to add additional rows to result set by condition

I have a table like this:
ObjId Date Value
100 '20100401' 12
200 '20100501' 45
200 '20100401' 37
300 '20100501' 75
300 '20100401' 69
400 '20100401' 87
I have to add additional rows to result set for objId's, where there is no data at '20100501'
**100 '20100501' null**
100 '20100401' 12
200 '20100501' 45
200 '20100401' 37
300 '20100501' 75
300 '20100401' 69
**400 '20100501' null**
400 '20100401' 87
What is the best way to do this?
Here is the T-SQL script for the initial table:
declare #datesTable table (objId int, date smalldatetime, value int)
insert #datesTable
select 100, '20100401', 12
union all
select 200, '20100501', 45
union all
select 200, '20100401', 37
union all
select 300, '20100501', 75
union all
select 300, '20100401', 69
union all
select 400, '20100401', 87
select * from #datesTable
OK, I understand now :-) You want to find the objId values which have no entry with a date of 2010-05-01 and then insert extra rows with those objId and that date and a NULL value - use a CTE (Common Table Expression):
;WITH MissingObj AS
(
SELECT objId
FROM #datesTable d1
WHERE NOT EXISTS (SELECT objId FROM #datesTable d2 WHERE d2.objId = d1.objId AND d2.date = '20100501')
)
INSERT INTO #datesTable(objId, date, value)
SELECT
mo.objId, '20100501', NULL
FROM
MissingObj mo
The MissingObj CTE gets all the objId values where there's no entry for '2010-05-01', and then using that list of objId, the following INSERT statement inserts those into your #datesTable table.
As a side-note: I find this approach much easier for filling up sample tables:
declare #datesTable table (objId int, date smalldatetime, value int)
insert into #datesTable(objId, date, value)
VALUES(100, '20100401', 12),
(200, '20100501', 45),
(200, '20100401', 37),
(300, '20100501', 75) -- and so forth
SQL Server 2008 allows you to pass in multiple tuples of values in (....) - much easier and more readable than a union all construct...
This can be a solution:
declare #datesTable table (objId int, date smalldatetime, value int)
insert #datesTable
select 100, '20100401', 12
union all
select 200, '20100501', 45
union all
select 200, '20100401', 37
union all
select 300, '20100501', 75
union all
select 300, '20100401', 69
union all
select 400, '20100401', 87
with objids as (
select distinct objid
from #datesTable )
insert into #datesTable(objId, date, value)
select D.objId, '20100501', NULL
from objids D
where not exists(select *
from #datestable T
where T.objId = D.objId
and date = '20100501')
select *from #datesTable