I have a table with a primary key field ID. I don't want to use Identity because i need to give the User the posibility of manualy choose an ID for the new object. So my idea is:
By default in the edit view, the ID field will be 0.
If user don't change it, i need to find the first free ID and use it.
If the user change the ID, i first need to check if there's another object with that id, and in that case throw an error.
If not, use the ID choose by user.
create the new object
The question is how to query and SQL Server table to get the firts free ID number?
Examples 1:
ID
--
1
2
10
First free ID is 3
Examples 2:
ID
--
1
2
3
4
First free ID is 5
Is there a way to do that?
All i can think of is get the min and max value, create a cycle for possible values and then compare with table data, but it involves too many querys to the database.
Thanks!
You can find the first free id as the first id where there is no "next" value:
select coalesce(min(t.id) + 1, 0)
from table t left outer join
table t2
on t.id = t2.id - 1
where t2.id is null;
EDIT:
If you want to handle "1" as a potential missing value:
select (case when min(minid) > 1 then 1 else coalesce(min(t.id) + 1, 0) end)
from table t left outer join
table t2
on t.id = t2.id - 1 cross join
(select min(id) as minid from table t) const
where t2.id is null;
Test Table
CREATE TABLE ID_TABLE(ID INT)
INSERT INTO ID_TABLE VALUES
(1),(2),(10)
Stored Procedure
ALTER PROCEDURE dbo.usp_GetNextValue
#nxt_ID_Wanted INT = 0,
#nxt_ID_Available INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
-- If user hasnt passed any value get next avilable value
IF (#nxt_ID_Wanted = 0)
BEGIN
SELECT TOP 1 #nxt_ID_Available = ID + 1
FROM
(
SELECT ID , ROW_NUMBER() OVER (ORDER BY ID ASC) AS rn
FROM ID_TABLE
)Q
WHERE ID = rn
ORDER BY ID DESC
IF (#nxt_ID_Available IS NULL)
BEGIN
SET #nxt_ID_Available = 1;
END
END
-- If user has passed a value check if it exists and raise error
ELSE IF EXISTS(SELECT 1 FROM ID_TABLE WHERE ID = #nxt_ID_Wanted)
BEGIN
RAISERROR('Selected ID value already exists',16,1)
SET #nxt_ID_Wanted = 0;
RETURN;
END
ELSE -- else just let the user have the value he/she wanted
BEGIN
SET #nxt_ID_Available = #nxt_ID_Wanted;
END
END
Execute Procedure
DECLARE #ID INT;
EXECUTE dbo.usp_GetNextValue #nxt_ID_Wanted = 6
,#nxt_ID_Available = #ID OUTPUT
SELECT #ID
This function gets first free ID for a table in PostgreSQL (PL/pgSQL):
CREATE OR REPLACE FUNCTION next_free_id_from(table_name TEXT)
RETURNS BIGINT AS
$$
DECLARE
next_id BIGINT;
BEGIN
execute 'select rn from (select id, row_number() over (order by id) as rn from ' || quote_ident(table_name) || ') as T where id > rn limit 1' into next_id;
if next_id is null then
execute 'select COALESCE(max(id), 0) + 1 from ' || quote_ident(table_name) into next_id;
end if;
return next_id;
END;
$$ language 'plpgsql' STRICT;
Execute:
select next_free_id_from('test');
Where test is a table name. This function call can be placed in DEFAULT of the primary key.
Related
I am trying to capture if my SQL Query have 0 rows or multiple rows. If it has 0 rows then I will insert, if 1 will perform an update, if > 1 will perform additional analysis.
Is there a way I can see if my query resulted in x results or no results in automation anywhere?
Any assistance will be appreciated.
You can make use of if exists and if not exists and check if rows exists or not, or even if there are multiple before doing the insert.
Here is a simple example using if not exists where if the row doesn't exist on dbo.Table it will insert a row. If it already exists then the ID will be logged to an Error table.
declare #InsertID int = 5, #Name nvarchar(max) = 'some name'
if ((select count(1) from dbo.Table where ID = #InsertID) > 1) -- detect error; more than one record for an id
begin
insert into dbo.Error (ErrorID, ErrorDate)
select #InsertID, getdate()
end
else if not exists (select 1 from dbo.Table where ID = #InsertID) -- no record exists for ID, insert it
begin
insert into dbo.Table (ID, Name)
select #InsertID, #Name
else if exists (select 1 from dbo.Table where ID = #InsertID) -- update the single record
begin
update dbo.Table set Name = #Name where ID = #InsertID
end
A2019 returns the results of a SQL Query as a table...
You could have an if statement right after your query which checks to see if the row count of the returned table is > 0 then take action accordingly.
For the selected rows in a column, how to update each row sequentially from the beginning to the end, with each row value incremented by 1 (or certain number). I know this can be done in excel in few seconds but I could figure out how to achieve in SQL server. For instance:
customer id is NULL now
update customer id with every row incremented by 1 (i.e. first row = 1, second row = 2, .....nth row = n)
ship-to party customer id
0002018092 NULL
0002008127 NULL
0002000129 NULL
0002031592 NULL
0002034232 NULL
desired output
ship-to party customer id
0002018092 1
0002008127 2
0002000129 3
0002031592 4
0002034232 5
Also, for the selected rows in a column, how to update each row with the row number? I know there is a row_number() function but didn't succeed in producing the desired result. for instance
column A is NULL now
update Column A with every row incremented by 1 (i.e. first row = row number 1, second row = row number 2, .....nth row = row number n)
Any demonstration would be very helpful.thkans
example : suppose I want to add a value to each value in column SomeIntField in table tblUser
there are 2 ways of doing this easy
first: this just adds value 1 to each column SomeIntField
update tblUser set SomeIntField = SomeIntField + 1
second : this adds an incrementing value, the first row gets +1, second gets +2, and so on...
declare #number int = 0
update tblUser
set #number = #number + 1,
SomeIntField = isnull(SomeIntField, 0) + #Number
EDIT: based on your last comment this might be what you want
declare #table table (shiptoparty varchar(50), customer_id int)
insert into #Table (shiptoparty, customer_id)
values ('0002018092', NULL), ('0002008127', NULL), ('0002000129', NULL), ('0002031592', NULL), ('0002034232', NULL)
declare #number int = 0
update #table
set #number = #number + 1,
customer_id = isnull(customer_id, 0) + #Number
select * from #table
The result of this is :
shiptoparty | customer_id
----------- | -----------
0002018092 | 1
0002008127 | 2
0002000129 | 3
0002031592 | 4
0002034232 | 5
Rather than using a self referencing variable, use a CTE:
WITH CTE AS (
SELECT [Your Incrementing Column],
ROW_NUMBER() OVER (ORDER BY [Columns to Order By]) AS RN
FROM YourTable)
UPDATE CTE
SET [Your Incrementing Column] = RN;
Edit: To prove a point that ALL rows will be updated:
CREATE TABLE #Sample (String varchar(50),
IncrementingInt int);
INSERT INTO #Sample (String)
VALUES ('sdkfjasdf'),
('dfydsfdfg'),
('sdfgsdfg45yfg'),
('dfgf54d'),
('dsft43tdc'),
('f6gytrntrfu7m45'),
('5d6f45wgby54'),
('g34h636j'),
('jw'),
('h6nw54m'),
('g54j747jm5e5f4w5gsft'),
('ns67mw54mk8o7hr'),
('h45j4w5h4');
SELECT *
FROM #Sample;
WITH CTE AS(
SELECT IncrementingInt,
ROW_NUMBER() OVER (ORDER BY String) AS RN
FROM #Sample)
UPDATE CTE
SET IncrementingInt = RN;
SELECT *
FROM #Sample;
DROP TABLE #Sample;
GO
To update each row with row number
Try below
CREATE TABLE tmp(Id INT IDENTITY(1,1), Value INT)
INSERT INTO tmp(value) VALUES(1),(2),(3),(4),(5)
UPDATE T
SET
T.Value = B.RowNo
FROM tmp AS T
INNER JOIN (SELECT Id, ROW_NUMBER()OVER(ORDER BY Id) AS RowNo FROM tmp)AS B
ON T.Id = B.Id
Don't think very complex. Try the simple method given below
alter table table_name drop column customer_id
go
alter table table_name add id customer_id IDENTITY(1,1)
go
First problem:
you want to increase values in every row in certain column by 1 (or other nuber), try this:
update TABLE_NAME set column_to_increase = column_to_increase + 1
Second problem:
you want to get row number for only certain rows. Solution: first create column holding all row numbers, then get the rows:
select * from (
select column1, column2, ..., columnN, row_number() over (order by (select null)) as [rn] from MY_TABLE
) where *condition*
FYI: select null in over clause does exactly nothing, it's just there, because window functions (such as row_number) have to have over clause and some of them require order by.
I have a table TBL_TEST which has a column NAME with 5 rows, and those rows have the values 'Balkhab', 'Gosfandi', 'Sancharak', 'Aqcha'
I want to write a query with a WHERE clause that when the value in where class match with one of the values in NAME column in TBL_TEST then it should show only that value but when it does not match then it should show all the values in the table, below is select statement that is not working as expect.
SELECT NAME
FROM TBL_TEST
WHERE NAME = 'Balkhab' OR NAME != 'Balkhab'
I suspect you want something like this:
SELECT NAME
FROM TBL_TEST
WHERE NAME = 'Balkhab'
UNION ALL
SELECT NAME
FROM TBL_TEST
WHERE NOT EXISTS (SELECT 1 FROM TBL_TEST WHERE NAME = 'Balkhab');
IF Exists(select 'X' FROM TBL_TEST WHERE NAME = 'Balkhab')
SELECT NAME from TBL_TEST WHERE NAME = 'Balkhab'
ELSE
SELECT NAME FROM TBL_TEST
You can try this one below
select *
from table where not exists (select name from table where name = 'Aqcha') or
name ='Aqcha'
You can write a stored procedure for it with one parameter.
Create procedure(#Name varchar(max))
As
begin
If(Isnull(#name, 0))
Begin
If exists (select 1 from TBL_TEST xx where xx.name = #name)
Begin
Select *
From TBL_TEST
Where name = #name
End
Else
Begin
Select *
From TBL_TEST
End
End
Else
Begin
Select * from TBL_TEST
End
End
Pass your name as parameter to the stored procedure to get require result
stored procedure are the best option as you can avoid network traffic, less bandwidth as you can pass only require parameter and get required result
I have a table of Users and if the is companyID that is input is equal to the companyID the User their name is shown and all the other names are shown as XXXXX. I need a way to set it to "Name"(literally the word name) and then an incremented number for how ever many there are.
select
case when t1.CompanyID = #id then t1.name else
(
'XXXXX'
)
end as Name
with input 148 shows
i want to show this, and so on for more names(name3, name4, etc..):
With foo being the name of your table, then this works (see sqlfiddle) :
SET #id = 142;
SET #i = 0;
select
companyID,
CASE WHEN foo.CompanyID = #id
THEN foo.name
ELSE (Concat('Name',#i := #i+1))
END as Name
FROM foo;
Try this please.
select [CompanyID],
case when([CompanyID]=#Id) then 'Name' +
convert(nvarchar,ROW_NUMBER() OVER(ORDER BY Name DESC)) else Name end as Name
from [dbo].[Table_1]
order by [CompanyID]
I would like to get all IDs from children in a tree with MySQL only.
I have a table like this:
ID parent_id name
1 0 cat1
2 1 subcat1
3 2 sub-subcat1
4 2 sub-subcat2
5 0 cat2
Now I'm trying to get all child IDs for cat1 (2,3,4) recursively. Is there any way how to achieve that?
There are two basic methods for doing this: adjacency lists and nested lists. Take a look at Managing Hierarchical Data in MySQL.
What you have is an adjacency list. No there isn't a way of recursively grabbing all descendants with a single SQL statement. If possible, just grab them all and map them all in code.
Nested sets can do what you want but I tend to avoid it because the cost of inserting a record is high and it's error-prone.
Here is a simple single-query MySql-solution:
SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
SELECT #Ids := (
SELECT GROUP_CONCAT(`ID` SEPARATOR ',')
FROM `table_name`
WHERE FIND_IN_SET(`parent_id`, #Ids)
) Level
FROM `table_name`
JOIN (SELECT #Ids := <id>) temp1
) temp2
Just substitute <id> with the parent element's ID.
This will return a string with the IDs of all descendants of the element with ID = <id>, separated by ,. If you would rather have multiple rows returned, with one descendant on each row, you can use something like this:
SELECT *
FROM `table_name`
WHERE FIND_IN_SET(`ID`, (
SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
SELECT #Ids := (
SELECT GROUP_CONCAT(`ID` SEPARATOR ',')
FROM `table_name`
WHERE FIND_IN_SET(`parent_id`, #Ids)
) Level
FROM `table_name`
JOIN (SELECT #Ids := <id>) temp1
) temp2
))
Including the root/parent element
The OP asked for the children of an element, which is answered above. In some cases it might be useful to include the root/parent element in the result. Here are my suggested solutions:
Comma-separated string of ids:
SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
SELECT <id> Level
UNION
SELECT #Ids := (
SELECT GROUP_CONCAT(`ID` SEPARATOR ',')
FROM `table_name`
WHERE FIND_IN_SET(`parent_id`, #Ids)
) Level
FROM `table_name`
JOIN (SELECT #Ids := <id>) temp1
) temp2
Multiple rows:
SELECT *
FROM `table_name`
WHERE `ID` = <id> OR FIND_IN_SET(`ID`, (
SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
SELECT #Ids := (
SELECT GROUP_CONCAT(`ID` SEPARATOR ',')
FROM `table_name`
WHERE FIND_IN_SET(`parent_id`, #Ids)
) Level
FROM `table_name`
JOIN (SELECT #Ids := <id>) temp1
) temp2
))
You could probably do it with a stored procedure, if that's an option for you.
Otherwise you can't do it with a single sql-statement.
Ideally you should make the recursive calls to walk the tree from your program
create table it should be look like below
DROP TABLE IF EXISTS `parent_child`;
CREATE TABLE `parent_child` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`parent_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
insert into `parent_child`(`id`,`name`,`parent_id`)
values (1,'cat1',0),(2,'subcat1',1),
(3,'sub-subcat1',2),(4,'sub-subcat2',2),
(5,'cat2',0);
Create function for getting parent child element
DELIMITER $$
USE `yourdatabase`$$
DROP FUNCTION IF EXISTS `GetAllNode1`$$
CREATE DEFINER=`root`#`localhost` FUNCTION `GetAllNode1`(GivenID INT) RETURNS TEXT CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE rv,q,queue,queue_children TEXT;
DECLARE queue_length,front_id,pos INT;
SET rv = '';
SET queue = GivenID;
SET queue_length = 1;
WHILE queue_length > 0 DO
SET front_id = queue;
IF queue_length = 1 THEN
SET queue = '';
ELSE
SET pos = LOCATE(',',queue) + 1;
SET q = SUBSTR(queue,pos);
SET queue = q;
END IF;
SET queue_length = queue_length - 1;
SELECT IFNULL(qc,'') INTO queue_children
FROM (SELECT GROUP_CONCAT(id) AS qc
FROM `parent_child` WHERE `parent_id` = front_id) A ;
IF LENGTH(queue_children) = 0 THEN
IF LENGTH(queue) = 0 THEN
SET queue_length = 0;
END IF;
ELSE
IF LENGTH(rv) = 0 THEN
SET rv = queue_children;
ELSE
SET rv = CONCAT(rv,',',queue_children);
END IF;
IF LENGTH(queue) = 0 THEN
SET queue = queue_children;
ELSE
SET queue = CONCAT(queue,',',queue_children);
END IF;
SET queue_length = LENGTH(queue) - LENGTH(REPLACE(queue,',','')) + 1;
END IF;
END WHILE;
RETURN rv;
END$$
DELIMITER ;
write query for desire output
SELECT GetAllNode1(id) FROM parent_child
or
SELECT GetAllNode1(id) FROM parent_child where id =1 //for specific parent's child element
Your question seems a bit imprecise. Why do you want to have them, and what do you mean by having them, "in a tree" ?
The table you've got IS (the relational way to represent) the tree.
If you want them "in a table" with rows that hold the pairs (ID 4 , ParentID 0), then you need your SQL engine's version of recursive SQL to do this, if that engine supports it.
I wouldn't know about MySQL specifically, but my understanding is that they once planned to implement recursive SQL using the same syntax as Oracle, i.e. with CONNECT BY.
If you look in your manual's table of contents for keywords such as "recursive queries" or "CONNECT BY", I imagine you should be able to find the answer.
(Sorry for not being able to provide a more ready-to-consume answer.)