Auto Generate Alphanumeric(AANNNN) id's in postgresql - sql

I Want Auto Generate Alphanumeric(AANNNN) id's in postgresql.
for eg=AA0001,AA9999,AB0001.
And the Order of value increment from right to left.

Create a SERIAL column (auto-incrementing), then create a computed column with the following definition:
CREATE TABLE t (
ID SERIAL,
CharID varchar(10) GENERATED ALWAYS AS (
CHR(65 + ID / 260000) ||
CHR(65 + MOD(ID / 10000, 26)) ||
lpad(MOD(id, 260000)::text, 4, '0')
) STORED
);
db<>fiddle
It's simple arithmetic: uppercase letters begin at character 66, and there are 26 of them. So the first two lines create those two, using integer division and modular division. Then use modular division again to get the final 4 digits.

Related

Split values from column based on the name of the column

I work with QGIS and PostgreSQL with PostGIS. I need help with dynamic queries for PostgreSQL.
Information is structured in tables that contain votes for parties, and other types of information like geographic area or election date.
Some columns contains values that have to be splitted among several parties. For example, we can have a column with name "PartyA_PartyB" and a value of 10, and it should be splitted 5 votes to PartyA and 5 votes to PartyB. Additionally we will have independent columns for PartyA and PartyB (separated), so we need to compute a column where we allocate the original PartyA + PartyA_PartyB/2.
So for example for the given the tables “Election Results” and "Parties":
create table election_results ("Country" text, "PartyA" text, "PartyB" text, "PartyC" text, "PartyA_PartyB" text);
insert into election_results
VALUES
('Argentina', 100, 10, 20, 2),
('Uruguay', 3, 5, 1, 0),
('Chile', 40, 200, 50, 10)
;
create table parties (party text);
insert into parties
VALUES
('PartyA'),
('PartyB'),
('PartyC'),
('PartyD'),
('PartyE')
;
I need to create a new table with a column where 'new' PartyA = PartyA + PartyA_PartyB/2 and 'new' PartyB = PartyB + PartyA_PartyB/2
So with previous data desired result is:
Country
PartyA
PartyB
PartyC
Argentina
101
11
20
Uruguay
3
5
1
Chile
45
205
50
In all cases the special characters that separates the names to be splitted is '_'.
We can have n parties in the column names (for example PartyA_PartyB_PartyD_PartyE). Votes have to be splitted among the n parties.
With my limited understanding I think iterate over the columns could be a solution, look for the '_' character and recalculate.
Note: Please store your values not as text but as a numeric type.
demo: db<>fiddle (2 joined colums)
demo: db<>fiddle (additional 3 joined columns)
Create your new table:
CREATE TABLE parties (
"Country" text,
"PartyA" numeric,
"PartyB" numeric,
"PartyC" numeric
);
Copy values for the "single" columns:
INSERT INTO parties
SELECT "Country", "PartyA", "PartyB", "PartyC"
FROM election_results;
Update the columns with a function
SELECT * FROM split_and_update_parties();
The function could look like this:
CREATE OR REPLACE FUNCTION split_and_update_parties()
RETURNS void
LANGUAGE plpgsql AS
$func$
DECLARE
i record;
j text;
n integer;
BEGIN
FOR i in
SELECT
column_name, -- 1
string_to_array(column_name, '_') -- 2
FROM information_schema.columns
WHERE table_name = 'election_results'
AND column_name ~ 'Party'
LOOP
n = cardinality(i.string_to_array); -- 3
IF n > 1 THEN
FOREACH j in array i.string_to_array LOOP
EXECUTE format('
UPDATE parties p -- 4
SET %I = p.%I + s.val / %s
FROM (
SELECT %I as val, "Country"
FROM election_results
) s
WHERE p."Country" = s."Country"
', j, j, n, i.column_name);
END LOOP;
END IF;
END LOOP;
END
$func$;
Explanation:
Fetch column names from internal information schema
Immediately split the names and convert them into arrays
Count the elements of the arrays to know the divider needed furtherly in the calculation
Loop through all these multiple-party-arrays/columns (with more than 1 element), fetch the original values from the election_results table and update the single-party-columns in the new table

Add Sequential Letter column to table

In C I can switch between int and char to build a list of sequential integers and then convert them to ASCII characters that will be sequential according to the alphabet.
Is there a way to do this in postgres to get a column with row values "A","B","C" etc...?
the only way I can think of would be to start with a SERIAL column of ints and then write a CASE to set each row individually. The Tables big enough that it would be much preferred to do this automatically somehow.
Take a look here: Calculate MS Excel column name from its number in PostgreSQL
CREATE FUNCTION excel_column(col integer)
RETURNS text AS
$BODY$
WITH RECURSIVE t(n, out) AS (
SELECT col/26-(col%26=0)::int, chr((col-1)%26 + 65)
UNION ALL
SELECT n/26-(n%26=0)::int, chr((n-1)%26 + 65) || out FROM t
where n>0
)
SELECT out FROM t where n=0;
$BODY$
LANGUAGE sql IMMUTABLE LEAKPROOF STRICT;
Usage:
select excel_column(x) from generate_series(1,800) x;
Result:
A
B
..
AA
AB
..
ZZ
AAA
..
ADT

Generate sequential number in SQL - not by using Identity

I am working on a task where my query will produce a fixed width column. One of the fields in the fixed width column needs to be a sequentially generated number.
Below is my query:
select
_row_ord = 40,
_cid = t.client_num,
_segment = 'ABC',
_value =
concat(
'ABC*',
'XX**', --Hierarchical ID number-this field should be sequentially generated
'20*',
'1*','~'
)
from #temp1 t
My output:
Is there a way to declare #num as a parameter that generates number sequentially?
PS: The fields inside the CONCAT function is all hardcoded. Only the 'XX' i.e., the sequential number has to be dynamically generated
Any help?!
You could create a SEQUENCE object, then call the NEXT VALUE FOR the SEQUENCE in your query.
Something along these lines:
CREATE SEQUENCE dbo.ExportValues
START WITH 1
INCREMENT BY 1 ;
GO
And then:
select
_row_ord = 40,
_cid = t.client_num,
_segment = 'ABC',
_value =
concat(
'ABC*',
RIGHT(CONCAT('000000000000000', NEXT VALUE FOR dbo.ExportValues),15)
'**',
'20*',
'1*','~'
)
from #temp1 t
You'd have to tweak how many zeros there are for the padding and how many digits to trim it to for your requirements. If duplicate values are ok, you could have the SEQUENCE reset periodically. See the documentation for more on that. It's just another line in the CREATE statement.
You can use row_number() -- made a little more complicated because you are zero-padding it:
select _row_ord = 40, _cid = t.client_num, _segment = 'ABC',
_value = concat('ABC*',
right('00' + convert(varchar(255), row_number() over (order by ?)), 2),
'XX**', --Hierarchical ID number-this field should be sequentially generated
'20*',
'1*','~'
)
from #temp1 t;
Note that the ? is for the column that specifies the ordering. If you don't care about the ordering of the numbers, use (select null) in place of the ?.

How to auto generate a ID with random numbers in sql server

For example I had a column named 'ID'
I want to get the output as
ID
---
ABCXX708
ABCXX976
ABCXX654
ABCXX081
In short ABCXX should be common for every row but the remaining 3 numbers should be random and integer..
with t (n) as (select 0 union all select n+1 from t where n <100)
select 'ABC'
+ format(n,'00')
+ cast(cast(rand(cast(newid() as varbinary(100)))*10 as int) as char(1))
from t
Alternative solution
with t (n) as (select 0 union all select n+1 from t where n <100)
select 'ABC'
+ right ('0' + cast(n as varchar(2)),2)
+ cast(cast(rand(cast(newid() as varbinary(100)))*10 as int) as char(1))
from t
You can write like this
select 'ABCXX'+CAST(FLOOR(RAND()*(1000-100)+100) as varchar(3)) 'id'
With the RAND() function you can get Random numbers. And For the 'ABCXX' you can follow your previous logic.
SELECT CAST(RAND()*10.00 AS INT)
The above RAND() function will give values between 0.0 to 1.0 in decimals every time you hit the Statement. To make it for a single digit Multiply with 10 and Cast it to INT to remove the next decimal values.
Reference " MSDN
Since SQL Server 2012 you have FORMAT function and SEQUENCE object. Hence the below query will work.
First you need to create a Sequence object.
CREATE SEQUENCE DemopSeq
START WITH 1
INCREMENT BY 1;
Then the following query will generate results as per your requirement.
SELECT CONCAT('ABC',FORMAT(NEXT VALUE FOR DemopSeq, '00'),ABS(Checksum(NewID()) % 10))
Hope this helps.

Teradata : Sum up values in a column

Problem Statement
Example is shown in below image :
The last 2 rows have the patterns like "1.283 2 3" in a single cell. The numbers are seperated by space in the column. We need to add those nos and represent in the format given in Output.
So, the cell having "1.283 2 3" must be converted to 6.283
Challenges facing :
The column values are in string format.
Add nos after casting them into integer
Donot want to take data in UNIX box and manipulate the same.
In TD14 there would be a built-in table UDF named STRTOK_SPLIT_TO_TABLE, before you need to implement your own UDF or use a recursive query.
I modified an existing string splitting script to use blanks as delimiter:
CREATE VOLATILE TABLE Strings
(
groupcol INT NOT NULL,
string VARCHAR(991) NOT NULL
) ON COMMIT PRESERVE ROWS;
INSERT INTO Strings VALUES (1,'71.792');
INSERT INTO Strings VALUES (2,'71.792 1 2');
INSERT INTO Strings VALUES (3,'1.283 2 3');
WITH RECURSIVE cte
(groupcol,
--string,
len,
remaining,
word,
pos
) AS (
SELECT
GroupCol,
--String,
POSITION(' ' IN String || ' ') - 1 AS len,
TRIM(LEADING FROM SUBSTRING(String || ' ' FROM len + 2)) AS remaining,
TRIM(SUBSTRING(String FROM 1 FOR len)) AS word,
1
FROM strings
UNION ALL
SELECT
GroupCol,
--String,
POSITION(' ' IN remaining)- 1 AS len_new,
TRIM(LEADING FROM SUBSTRING(remaining FROM len_new + 2)),
TRIM(SUBSTRING(remaining FROM 1 FOR len_new)),
pos + 1
FROM cte
WHERE remaining <> ''
)
SELECT
groupcol,
-- remove the NULLIF to get 0 for blank strings
SUM(CAST(NULLIF(word, '') AS DECIMAL(18,3)))
FROM cte
GROUP BY 1
This might use a lot of spool, hopefully you're not running that on a large table.