SQL Server: INSERT INTO SELECT MAX - sql

I want to insert multi rows from another table. The problem is that I want to get the Max + 1 before I insert. Note that I know I should use Identify etc... However, I have this complex scenario of offline database synchronization across nodes...
INSERT INTO Purchase_Deliveries_Items
(ID,Item_ID)
SELECT
(SELECT
MAX(ID)+1 -- same MAX ID for all (the problem)
FROM
Purchase_Deliveries_Items),
Item_ID,
FROM
Purchase_Orders_Items
WHERE
PurchaseOrder_ID = 1

You can get a newid based on the max existing id with the help of ROW_NUMBER
INSERT INTO Purchase_Deliveries_Items (
ID,
Item_ID
)
SELECT
ROW_NUMBER() OVER (
ORDER BY
Item_ID
) + (SELECT MAX(ID)
FROM Purchase_Deliveries_Items) newID,
Item_ID,
FROM Purchase_Orders_Items
WHERE
PurchaseOrder_ID = 1

Seems like you want to do a kind of sequence, use a ROW_NUMBER:
INSERT INTO Purchase_Deliveries_Items
(ID,Item_ID)
SELECT (
SELECT MAX(ID) -- same MAX ID for all
FROM Purchase_Deliveries_Items
) + ROW_NUMBER() OVER (ORDER BY any_column),
Item_ID,
FROM Purchase_Orders_Items
WHERE PurchaseOrder_ID = 1

Related

How to group and pick only certain values based on a field using select query SQL

I have a table as follow
ID
ORDERNO
1
123
1
123
2
456
2
456
During every select query done via application using JDBC, only the grouped records based on ORDERNO should be picked.
That means, for example, during first select query only details related to ID = 1, but we cannot specify the ID number in where clause because we do not know how many number of IDs will be there in future. So the query should yield only one set of records; application will delete those records after picking, hence next select query will result in picking other set of records. How to achieve it?
You can use TOP WITH TIES for this
SELECT TOP (1) WITH TIES
t.ID,
t.ORDERNO
FROM YourTable t
ORDER BY
t.ID;
If you want to select and delete at the same time you could delete using an OUTPUT clause
WITH cte AS (
SELECT TOP (1) WITH TIES
t.ID,
t.ORDERNO
FROM YourTable t
ORDER BY
t.ID
)
DELETE cte
OUTPUT deleted.*;
As one option you could select on the MIN(ID) like:
SELECT *
FROM yourtable
WHERE ID = (SELECT MIN(ID) FROM yourtable);
You could also use window functions to do this:
SELECT ID, ORDERNO
FROM
(
SELECT ID, ORDERNO
DENSE_RANK() OVER (ORDER BY ID ASC) AS dr
FROM yourtable
)dt
WHERE dr = 1;
order your rows and select top n number of rows that you want :
select top (1) with ties ID, ORDERNO
from tablename
order by ID asc

SQL: How can I select top 1 row, based on different criteria on same column

Say I have a table which has a column named ItemCode, it has fixed format xxx-xxxx where x is [0-9], for example a possible value of ItemCode is 097-1234
Now I would like to select the largest ItemCode which starts with 987 AND the last ItemCode starts with 123, so I am trying to do something like (This is wrong)
SELECT TOP 1 ItemCode From Table
WHERE ItemCode like '987%' OR ItemCode like '123%'
ORDER BY 1 DESC
So how can I write a SQL which can select the last ItemCode of each criteria? Is there any general method which can extend to select top N rows on M such criterias?
(assuming there exists data fulfills the criteria, here 2 rows should be returned: largest ItemCode starts with 987 and largest ItemCode starts with 123)
Another option without UNION, you could use TOP 1 WITH TIES and ROW_NUMBER() OVER() like this
SELECT TOP 1 WITH TIES *
From YourTable
WHERE ItemCode like '987%' OR ItemCode like '123%'
ORDER BY ROW_NUMBER() OVER(PARTITION BY LEFT(ItemCode,3) ORDER BY Itemcode DESC)
You can use ROW_NUMBER() in a CTE for a more generalised form:
;With Ordered as (
select
*,
ROW_NUMBER() OVER (
PARTITION BY SUBSTRING(ItemCode,1,3)
ORDER BY ItemCode desc) as rn
from
Table
where
ItemCode like '987%' or
ItemCode like '123%'
)
select *
from Ordered
where rn = 1
As I alluded to in the comments, if possible I'd change the structure so that the ItemCode elements are separately stored, which would make for a simpler internal query form that could also more easily benefit from indexes. E.g. something like:
;With Ordered as (
select
*,
ROW_NUMBER() OVER (
PARTITION BY ItemCode_Prefix
ORDER BY ItemCode_Suffix desc) as rn
from
Table
where
ItemCode_Prefix in (987,123)
)
select *
from Ordered
where rn = 1
Thanks to #jarlh, I used UNION to achieve what I need.
If anyone has a more general method which may easier to be extended to more criteria, please post an answer and I will accept it. Cheers.
SELECT * FROM(
SELECT TOP 1 ItemCode FROM Table
WHERE ItemCode LIKE '987%'
ORDER BY 1 DESC
) AS A
UNION
SELECT * FROM(
SELECT TOP 1 ItemCode FROM Table
WHERE ItemCode LIKE '123%'
ORDER BY 1 DESC
) AS B
Complete solution based on ROW_NUMBER() function:
use tempdb;
go
-- Test data
create table #test_data
(ItemCode char(8) not null);
insert into #test_data
values
('097-1234'),
('097-1243'),
('097-7890'),
('012-1234'),
('912-1234'),
('123-1234'), -- second max for '987,123'
('123-1234'),
('123-0001'),
('123-0932'),
('987-1234'),
('987-5643'),
('987-7890'), -- first max for '987,123'
('000-7890');
go
-- Test data
-- Code
create proc dbo.top_n_from_m
#criterias varchar(max)
as
set nocount on;
declare #crs table
(id int not null identity (1, 1) primary key,
string char(3) not null);
insert into #crs (string)
select value
from string_split(#criterias, ',')
select t.ItemCode
from
(select t.ItemCode,
c.id,
row_id = row_number() over (partition by c.id order by t.ItemCode desc)
from #test_data as t
join #crs as c on t.ItemCode like c.string + '-%') as t
where t.row_id = 1
order by t.id
go
-- Code
-- Test
execute dbo.top_n_from_m #criterias = '987,123'
select ItemCode
from #test_data
order by ItemCode
-- Test
-- Clear
drop table #test_data;
drop proc dbo.top_n_from_m;
-- Clear

SQL select only one item which meets condition

I have a very narrow table: DATA, ID, LAT, LNG, TIME.
(https://gyazo.com/52b268c00963ed12ba85c6765f40bf63)
And I want to select the newest data for each different ID. I was using query like
SELECT *
FROM name_of_table
WHERE TIME > my_given_time;
but it selects TOTALLY all datas and not only data for each different id which meets the condition.
Could somebody please help me write the query?
Thank you for any help. :)
EDIT
The final look of my working query looks like:
SELECT * FROM (SELECT * FROM (SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY TIME DESC) AS ROWNUMBER, * FROM my_table) WHERE ROWNUMBER = 1) WHERE TIME > my_time;
Thanks everyone for help
How about something like this
SELECT ID, DATA
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY TIME DESC) AS ROWNUMBER, ID, DATA
FROM name_of_table)
WHERE ROWNUMBER = 1;
For these dummy records
INSERT INTO name_of_table (ID, TIME, DATA) VALUES('test',1230,16);
INSERT INTO name_of_table (ID, TIME, DATA) VALUES('test2',1235,10);
INSERT INTO name_of_table (ID, TIME, DATA) VALUES('test',1234,20);
the query returns the data value for the largest timestamp for each ID
ID DATA
----- ----
test 20
test2 10
I would do something like this:
SELECT tt.* FROM tblTest tt
WHERE _ID IN (
SELECT TOP 1 _ID FROM tblTest WHERE ID = tt.ID ORDER BY TIME DESC
)
Assuming _ID that i found in your linked picture is a unique identifier for each individual row
Try this:
SELECT a.*
FROM `sometable` a
JOIN (SELECT MAX(TIME) AS `max_time`,`ID` FROM `sometable` group by `ID`) b
ON b.`ID` = a.`ID` AND a.`TIME` = b.`max_time`;

SQL Server sequence

I want to create a sequence group by a particular column.
I have a table LoanMaster and there is a column called BranchCode. It has branches 1 - 48. I want to generate a sequence number per branch. Like for branch code 1, if it has 10 records then generate a sequence number from 1 - 10, then for branch code 2 if it has 15 records generate number from 1 - 15 and so on.
How is it possible?
Use ROW_NUMBER:
SELECT
BranchCode,
[rn] = ROW_NUMBER() OVER(PARTITION BY BranchCode ORDER BY BranchCode)
FROM LoanMaster
ORDER BY BranchCode, rn
#EDIT:#
To start from arbitrary value just add it:
SqlFiddleDemo
SELECT
BranchCode,
[rn] = ROW_NUMBER() OVER(PARTITION BY BranchCode ORDER BY BranchCode) + 100
FROM LoanMaster
ORDER BY BranchCode, rn
#EDIT 2:#
Demo2
CREATE TABLE #LoanMaster(Id INT, LeadsID1 INT, LBrCode INT);
INSERT INTO #LoanMaster(Id, LBrCode)
VALUES (1,1), (2,1), (3,1), (4,1), (5,2), (6,2), (7,2);
;WITH cte AS
(
SELECT
Id,
LBrCode,
[RowNumber] = ROW_NUMBER() OVER(PARTITION BY LBrCode ORDER BY LBrCode) + 2405
FROM #LoanMaster
)
UPDATE T1
SET LeadsID1=c.RowNumber
FROM #LoanMaster AS T1
JOIN cte c
ON c.LBrCode=T1.LBrCode
AND c.Id = T1.Id
WHERE c.LBrCode=1;
SELECT *
FROM #LoanMaster;
This is my last update (accept answer or not) because initial question was answered long ago, for future:
you should ask specific question
provide actual table structures + SQLFiddle
provide desired output
specify what you want to achieve query/update
specify edge cases
Writing in comments more and more demands is not how SO works.

Get most commonly occurring value for each user id

I have a table with userIds and product categories prod. I want to get a table of unique userIds and associated most occurring product categories prod. In other words, I want to know what item categorys each customer is buying the most. How can I achieve this in PL/SQL or Oracle SQL?
|userId|prod|
|------|----|
|123544|cars|
|123544|cars|
|123544|dogs|
|123544|cats|
|987689|bats|
|987689|cats|
I have already seen SO questions for getting the most common value of a column, but how do I get the most common value for each unique userId?
You should use just SQL to solve this .. if you really need it in pl/sql, just imbed this query within plsql ..
(setup)
drop table yourtable;
create table yourtable (
userID number,
prod varchar2(10)
)
/
insert into yourtable values ( 123544, 'cars' );
insert into yourtable values ( 123544, 'cars' );
insert into yourtable values ( 123544, 'dogs' );
insert into yourtable values ( 123544, 'cats' );
insert into yourtable values ( 987689, 'bats' );
insert into yourtable values ( 987689, 'cats' );
commit;
-- assuming ties are not broken, this logic returns both ties
with w_grp as (
select userID, prod, count(*) over ( partition by userID, prod ) rgrp
from yourtable
),
w_rnk as (
select userID, prod, rgrp,
rank() over (partition by userID order by rgrp desc) rnk,
from w_grp
)
select distinct userID, prod
from w_rnk
where rnk = 1
/
USERID PROD
---------- ----------
987689 bats
987689 cats
123544 cars
-- assuming you just want 1 .. this will return 1 random one if they are tied. (ie this time it pulled 987689 bats, next time it might pull 987689 cats. It will always return 123544 cars, however, since there is no tie for that one.
with w_grp as (
select userID, prod, count(*) over ( partition by userID, prod ) rgrp
from yourtable
),
w_rnk as (
select userID, prod, rgrp,
row_number() over (partition by userID order by rgrp desc) rnum
from w_grp
)
select userID, prod, rnum
from w_rnk
where rnum = 1
/
USERID PROD RNUM
---------- ---------- ----------
123544 cars 1
987689 bats 1
[edit] Cleaned up unused rank/row_number from functions to avoid confusion [/edit]
SELECT user_id, prod, prod_cnt FROM (
SELECT user_id, prod, prod_cnt
, RANK() OVER ( PARTITION BY user_id ORDER BY prod_cnt DESC ) AS rn
FROM (
SELECT user_id, prod, COUNT(*) AS prod_cnt
FROM mytable
GROUP BY user_id, prod
)
) WHERE rn = 1;
In the innermost subquery I am getting the COUNT of each product by user. Then I rank them using the analytic (window) function RANK(). Then I simply select all of those where the RANK is equal to 1. Using RANK() instead of ROW_NUMBER() ensures that ties will be returned.