sql server combine values in two tables - sql-server

I am new to SQL in general and even newer to MS SQL. I apologize if the title isn't clear on what I want.
I have two tables an old one that I want to derive data from into a new table. The tables have the exact same columns but different number of rows. The new table has multiple copies of each value in the old table, which only has 2 occurences. see below comparison of two columns: letter and amount.
New table:
A 0
A 0
A 0
B 0
B 0
old table:
A 12
A 0
B 10
B 0
C 23
What I want to achieve is adding the values of the amount column from the old table into just the first occurence of the leter in the new table like so:
A 12
A 0
A 0
B 10
B 0
Inner join causes all the values to be filled ( so all the A's are set to 12).

try this:
DECLARE #test1 TABLE(col1 varchar(2),idn int)
insert into #test1
VALUES('A',0),
('A',0),
('A',0),
('B',0),
('B',0)
DECLARE #test2 TABLE(col1 varchar(2),idn int)
insert into #test2
VALUES('A',12),
('A',0),
('B',10),
('B',0),
('C',23)
;WITH CTE as (select *,ROW_NUMBER() over (partition by col1 order by col1) as rn from #test1)
update c SET c.idn=b.idn
from CTE c inner join (select col1,SUM(idn) as idn from #test2
group by col1) b
on c.col1 = b.col1
where c.rn=1
select * from #test1

click here to see Demo
declare #t table
(
val varchar(2),
digit int
)
insert into #t(val, digit)values('A', 0)
insert into #t(val, digit)values('A', 0)
insert into #t(val, digit)values('A', 0)
insert into #t(val, digit)values('B', 0)
insert into #t(val, digit)values('B', 0)
declare #t1 table
(
val varchar(2),
digit int
)
insert into #t1(val, digit)values('A', 12)
insert into #t1(val, digit)values('A', 0)
insert into #t1(val, digit)values('B', 10)
insert into #t1(val, digit)values('B', 0)
insert into #t1(val, digit)values('C', 23)
Select k.val, isNull(sum(k.digit + k1.digit), 0) as Digit from
(
Select ROW_NUMBER() over(partition by val order by val) as rowid, * from #t
)K
Left Join
(
Select ROW_NUMBER() over(partition by val order by val) as rowid, * from #t1
)K1
on k.val = k1.val AND K.rowid = K1.rowid
group by k.val, K.rowid

Related

Order by first row,last row and rest with 2 tables in SQL Server 2017

I have following two tables. I need to merge 2 tables based also sorted on
few conditions.
DROP TABLE IF EXISTS #t1
CREATE TABLE #t1
(
id INT IDENTITY PRIMARY KEY,
value INT
)
DROP TABLE IF EXISTS #t2
CREATE TABLE #t2
(
id INT IDENTITY PRIMARY KEY,
value int
)
INSERT INTO #t1 (value)
VALUES (1), (30), (19), (10), (40);
INSERT INTO #t2 (value)
VALUES (100), (70), (20);
SELECT * FROM #t1
UNION
SELECT * FROM #t2
I need to union 2 tables and sorted value based on below condition
#t1 first row (fixed)
#t1 last row (fixed)
#t1 rest (other than first row) based on value
4 #t2 only sorted based on value
Expected output:
why are you using varchar to store numeric value ? you will need to perform a cast() or convert() before you will be to sort it.
Assumption : by first or last you are referring with row with minimum and maximum value in column id.
select value, seq as [order]
from
(
select id, value,
seq = case when row_number() over(order by id) = 1 then 1
when row_number() over(order by id desc) = 1 then 2
else 3
end
from #t1
union all
select id, value, seq = 4
from #t2
) d
order by seq, convert(int, value)
You can check this below logic of ordering the output using CASE statement in the ORDER clause-
SELECT *
FROM
(
select 'T1' T_Name,* from #t1
union
select 'T2',* from #t2
)A
ORDER BY
CASE
WHEN t_name = 'T1' AND id = (SELECT MIN(ID) FROM #t1) THEN 1
WHEN t_name = 'T2' AND id = (SELECT MAX(ID) FROM #t2) THEN 2
WHEN t_name = 'T1' THEN 3
ELSE 4
END,
Id -- Or you can order by Value. Just keep in mind that Value is VARCHAR as per your setup. So, Ordering will be also impact accordingly.

How to rank one data set using another in SQL?

I have a table with float values. I want to use a subset (A) of those values to create ranks using PERCENT_RANK(). Then I want to assign ranks to a second (non-intersecting) subset (B) of values in the table based on the ranks derived from the first subset (A). Simply joining on values from (B) to values in (A) won't work since the values from subset (B) in general won't equal values in subset (A). In that case, I'm fine using either a "closest value" approach or a "linear interpolation" approach to get the ranks. My preference is for speed and simplicity since I'm dealing with hundreds of thousands of rows.
Here is a concrete example (assume subset A is where Flag = 0 and subset B is where Flag = 1):
DECLARE #Data TABLE
(
Value FLOAT,
Flag BIT
)
INSERT INTO #Data SELECT 0.081, 0
INSERT INTO #Data SELECT 0.831, 0
INSERT INTO #Data SELECT 0.798, 0
INSERT INTO #Data SELECT 0.722, 0
INSERT INTO #Data SELECT 0.322, 0
INSERT INTO #Data SELECT 0.186, 0
INSERT INTO #Data SELECT 0.494, 0
INSERT INTO #Data SELECT 0.757, 0
INSERT INTO #Data SELECT 0.996, 0
INSERT INTO #Data SELECT 0.146, 0
INSERT INTO #Data SELECT 0.514, 1
INSERT INTO #Data SELECT 0.787, 1
INSERT INTO #Data SELECT 0.125, 1
INSERT INTO #Data SELECT 0.324, 1
INSERT INTO #Data SELECT 0.86, 1
--Subset A
SELECT *,
Rnk = PERCENT_RANK() OVER (ORDER BY Value)
FROM #Data
WHERE Flag = 0
--Subset B
SELECT *,
Rnk = ?--Ranking based on ranks derived from subset A
FROM #Data
WHERE Flag = 1
Hmmm . . . This is one way:
with a as (
select d.*
PERCENT_RANK() OVER (ORDER BY Value) as rnk
from #Data d
where Flag = 0
)
select b.*, a.rnk
from #Data b outer join
(select top 1 *
from a
where a.value <= b.value
order by a.value desc
) a
where Flag = 1;

SQL Server: How do I get the highest value not set of an int column?

Let's take an example. These are the rows of the table I want get the data:
The column I'm talking about is the reference one. The user can set this value on the web form, but the system I'm developing must suggest the lowest reference value still not used.
As you can see, the smallest value of this column is 35. I could just take the smaller reference and sum 1, but, in that case, the value 36 is already used. So, the value I want is 37.
Is there a way to do this without a loop verification? This table will grow so much.
This is for 2012+
DECLARE #Tbl TABLE (id int, reference int)
INSERT INTO #Tbl
( id, reference )
VALUES
(1, 49),
(2, 125),
(3, 35),
(4, 1345),
(5, 36),
(6, 37)
SELECT
MIN(A.reference) + 1 Result
FROM
(
SELECT
*,
LEAD(reference) OVER (ORDER BY reference) Tmp
FROM
#Tbl
) A
WHERE
A.reference - A.Tmp != -1
Result: 37
Here is yet another place where the tally table is going to prove invaluable. In fact it is so useful I keep a view on my system that looks like this.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a cross join E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a cross join E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
Next of course we need some sample data and table to hold it.
create table #Something
(
id int identity
, reference int
, description varchar(10)
)
insert #Something (reference, description)
values (49, 'data1')
, (125, 'data2')
, (35, 'data3')
, (1345, 'data4')
, (36, 'data5')
, (7784, 'data6')
Now comes the magic of the tally table.
select top 1 t.N
from cteTally t
left join #Something s on t.N = s.reference
where t.N >= (select MIN(reference) from #Something)
and s.id is null
order by t.N
This is ugly, but should get the job done:
select
top 1 reference+1
from
[table]
where
reference+1 not in (select reference from [table])
order by reference
I used a table valued express to get the next value. I first left outer joined the table to itself (shifting the key in the join by +1). I then looked only at rows that had no corresponding match (b.ID is null). The minimum a.ReferenceID + 1 gives us the answer we are looking for.
create table MyTable
(
ID int identity,
Reference int,
Description varchar(20)
)
insert into MyTable values (10,'Data')
insert into MyTable values (11,'Data')
insert into MyTable values (12,'Data')
insert into MyTable values (15,'Data')
-- Find gap
;with Gaps as
(
select a.Reference+1 as 'GapID'
from MyTable a
left join MyTable b on a.Reference = b.Reference-1
where b.ID is null
)
select min(GapID) as 'NewReference'
from Gaps
NewReference
------------
13
I hope the code was clearer than my description.
CREATE TABLE #T(ID INT , REFERENCE INT, [DESCRIPTION] VARCHAR(50))
INSERT INTO #T
SELECT 1,49 , 'data1' UNION ALL
SELECT 2,125 , 'data2' UNION ALL
SELECT 3,35 , 'data3' UNION ALL
SELECT 4,1345, 'data4' UNION ALL
SELECT 5,36 , 'data5' UNION ALL
SELECT 6,7784, 'data6'
SELECT TOP 1 REFERENCE + 1
FROM #T T1
WHERE
NOT EXISTS
(
SELECT 1 FROM #T T2 WHERE T2.REFERENCE = T1.REFERENCE + 1
)
ORDER BY T1.REFERENCE
--- OR
SELECT MIN(REFERENCE) + 1
FROM #T T1
WHERE
NOT EXISTS
(
SELECT 1 FROM #T T2 WHERE T2.REFERENCE = T1.REFERENCE + 1
)
How about using a Tally table. The following illustrates the concept. It would be better to use a persisted numbers table as opposed to the cte however the code below illustrates the concept.
For further reading as to why you should use a persisted table, check out the following link: sql-auxiliary-table-of-numbers
DECLARE #START int = 1, #END int = 1000
CREATE TABLE #TEST(UsedValues INT)
INSERT INTO #TEST(UsedValues) VALUES
(1),(3),(5),(7),(9),(11),(13),(15),(17)
;With NumberSequence( Number ) as
(
Select #start as Number
union all
Select Number + 1
from NumberSequence
where Number < #end
)
SELECT MIN(Number)
FROM NumberSequence n
LEFT JOIN #TEST t
ON n.Number = t.UsedValues
WHERE UsedValues IS NULL
OPTION ( MAXRECURSION 1000 )
You could try using a descending order:
SELECT DISTINCT reference
FROM `Resultsados`
ORDER BY `reference` ASC;
As far as I know, there is no way to do this without a loop. To prevent multiple values from returning be sure to use DISTINCT.

Check if table does not have records then insert 3 records in table

I want to Check if table does not have records then only insert 3 records in table for sql server as well as oracle.
This sort of construct will work in SQL Server and Oracle:
SQL> insert into t34
2 select * from emp where id <= 3
3 and 0 in ( select count(*) from t34 )
4 /
3 rows created.
SQL> r
1 insert into t34
2 select * from emp where rownum <= 3
3* and 0 in ( select count(*) from t34 )
0 rows created.
SQL>
But whether it can solve your problem really depends on the source of your three rows.
what was the problem with doing this ..
try from your part..
int count = select count(*) from table-name;
if(count==0){
//your insert statements as many as
insert into table-name values();
}
You just need to do something like this:
IF (Select count(*) from tablename) = 0
BEGIN
-- INSERT VALUES
END
if you want to insert values in a single insert sentence you can do like this:
insert into yourTable (yourFields)
select value1 as yourField1, ...
where (select count(*) from yourTable ) =0
Testing:
create table #t (k int);
insert into #t
select 1
where (select count(*) from #t ) =0
insert into #t
select 2 as k;
insert into #t
select 3
where (select count(*) from #t ) =0
select * from #t;
Notice that only '1' and '2' are inserted, not '3'.

How to join sequential numbers to unrelated data (SQL Server)

This question is a followup to a previous question I had about discovering unused sequential number ranges without having to resort to cursors (Working with sequential numbers in SQL Server 2005 without cursors). I'm using SQL Server 2005.
What I need to do with those numbers is to assign those numbers to records in a table. I just can't seem to come up with a way to actually relate the numbers table with the records that need those numbers.
One possible solution that came to mind was insert the records in a temp table using an identity and using the beginning of the number range as an identity seed. The only problem with this approach is that if there are gaps in the number sequence then I'll end up with duplicate control numbers.
This is how my tables look like (overly simplified):
Numbers table:
Number
-------
102314
102315
102319
102320
102324
102329
Data table:
CustomerId PaymentAmt ControlNumber
---------- ---------- -------------
1001 4502.01 NULL
1002 890.00 NULL
9830 902923.34 NULL
I need a way to make it so i end up with:
CustomerId PaymentAmt ControlNumber
---------- ---------- -------------
1001 4502.01 102314
1002 890.00 102315
9830 902923.34 102319
Is this possible without having to use cursors? The reason I'm avoiding cursors is because our current implementation uses cursors and since its so slow (8 minutes over 12,000 records) I was looking for alternatives.
Note: Thanks to all who posted answers. All of them were great, I had to pick the one that seemed easier to implement and easiest to maintain for whomever comes after me. Much appreciated.
Try this:
;WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER(ORDER BY CustomerId) Corr
FROM DataTable
)
UPDATE CTE
SET CTE.ControlNumber = B.Number
FROM CTE
JOIN ( SELECT Number, ROW_NUMBER() OVER(ORDER BY Number) Corr
FROM NumberTable) B
ON CTE.Corr = B.Corr
Buidling on Martin's code from the linked question, you could give all rows without control number a row number. Then give all unused numbers a row number. Join the two sets together, and you get a unique number per row:
DECLARE #StartRange int, #EndRange int
SET #StartRange = 790123401
SET #EndRange = 790123450;
; WITH YourTable(ControlNumber, CustomerId) AS
(
SELECT 790123401, 1000
UNION ALL SELECT 790123402, 1001
UNION ALL SELECT 790123403, 1002
UNION ALL SELECT 790123406, 1003
UNION ALL SELECT NULL, 1004
UNION ALL SELECT NULL, 1005
UNION ALL SELECT NULL, 1006
)
, YourTableNumbered(rn, ControlNumber, CustomerId) AS
(
select row_number() over (
partition by IsNull(ControlNumber, -1)
order by ControlNumber)
, *
from YourTable
)
, Nums(N) AS
(
SELECT #StartRange
UNION ALL
SELECT N+1
FROM Nums
WHERE N < #EndRange
)
, UnusedNums(rn, N) as
(
select row_number() over (order by Nums.N)
, Nums.N
from Nums
where not exists
(
select *
from YourTable yt
where yt.ControlNumber = Nums.N
)
)
select ytn.CustomerId
, IsNull(ytn.ControlNumber, un.N)
from YourTableNumbered ytn
left join
UnusedNums un
on un.rn = ytn.rn
OPTION (MAXRECURSION 0)
All you need is a deterministic order in data table. If you have that, you can use ROW_NUMBER() as a join condition:
with cte as (
select row_number() over (order by CustomerId) as [row_number],
ControlNumber
from [Data Table]
where ControlNumber is null),
nte as (
select row_number() over (order by Number) as [row_number],
Number
from [Numbers])
update cte
set ControlNumber = Number
from cte
join nte on nte.[row_number] = cte.[row_number];
If you need it to be concurency proof, it does get more complex.
EDITED added in code to remove used values from #Number, via the OUTPUT caluse of the UPDATE and a DELETE
try using ROW_NUMBER() to join them:
DECLARE #Number table (Value int)
INSERT #Number VALUES (102314)
INSERT #Number VALUES (102315)
INSERT #Number VALUES (102319)
INSERT #Number VALUES (102320)
INSERT #Number VALUES (102324)
INSERT #Number VALUES (102329)
DECLARE #Data table (CustomerId int, PaymentAmt numeric(10,2),ControlNumber int)
INSERT #Data VALUES (1001, 4502.01 ,NULL)
INSERT #Data VALUES (1002, 890.00 ,NULL)
INSERT #Data VALUES (9830, 902923.34 ,NULL)
DECLARE #Used table (Value int)
;WITH RowNumber AS
(
SELECT Value,ROW_NUMBER() OVER(ORDER BY Value) AS RowNumber FROM #Number
)
,RowData AS
(
SELECT CustomerId,ROW_NUMBER() OVER(ORDER BY CustomerId) AS RowNumber, ControlNumber FROM #Data WHERE ControlNumber IS NULL
)
UPDATE d
SET ControlNumber=r.Value
OUTPUT r.Value INTO #Used
FROM RowData d
INNER JOIN RowNumber r ON d.RowNumber=r.RowNumber
DELETE #Number WHERE Value IN (SELECT Value FROM #Used)
SELECT * FROM #Data
SELECT * FROM #Number
OUTPUT:
CustomerId PaymentAmt ControlNumber
----------- --------------------------------------- -------------
1001 4502.01 102314
1002 890.00 102315
9830 902923.34 102319
(3 row(s) affected)
Value
-----------
102320
102324
102329
(3 row(s) affected)
You'll need something to join the two tables together. Some data value that you can match between the two tables.
I'm assuming there's more to your numbers table than just one column of numbers. If there's anything in there that you can match to your data table you can get away with an update.
How are you updating the data table using cursors?

Resources