Adding string to the primary key? - sql

I want to add some string with the primary key value while creating the table in sql?
Example:
my primary key column should automatically generate values like below:
'EMP101'
'EMP102'
'EMP103'
How to achieve it?

Try this: (For SQL Server 2012)
UPDATE MyTable
SET EMPID = CONCAT('EMP' , EMPID)
Or this: (For SQL Server < 2012)
UPDATE MyTable
SET EMPID = 'EMP' + EMPID
SQLFiddle for SQL Server 2008
SQLFiddle for SQL Server 2012

Since you want to set auto increment in VARCHAR type column you can try this table schema:
CREATE TABLE MyTable
(EMP INT NOT NULL IDENTITY(1000, 1)
,[EMPID] AS 'EMP' + CAST(EMP AS VARCHAR(10)) PERSISTED PRIMARY KEY
,EMPName VARCHAR(20))
;
INSERT INTO MyTable(EMPName) VALUES
('AA')
,('BB')
,('CC')
,('DD')
,('EE')
,('FF')
Output:
| EMP | EMPID | EMPNAME |
----------------------------
| 1000 | EMP1000 | AA |
| 1001 | EMP1001 | BB |
| 1002 | EMP1002 | CC |
| 1003 | EMP1003 | DD |
| 1004 | EMP1004 | EE |
| 1005 | EMP1005 | FF |
See this SQLFiddle
Here you can see EMPID is auto incremented column with Primary key.
Source: HOW TO SET IDENTITY KEY/AUTO INCREMENT ON VARCHAR COLUMN IN SQL SERVER (Thanks to #bvr)

What the rule of thumb is, is that never use meaningful information in primary keys (like Employee Number / Social Security number). Let that just be a plain autoincremented integer. However constant the data seems - it may change at one point (new legislation comes and all SSNs are recalculated).

it seems the only reason you are want to use a non-integer keys is that the key is generated as string concatenation with another column to make it unique.
From a best practice perspective, it is strongly recommended that integer primary keys are used, but often, this guidance is ignored.
May be going through the following posts might be of help:
Should I design a table with a primary key of varchar or int?
SQL primary key: integer vs varchar

You can achieve it at least in two ways:
Generate new id on the fly when you insert a new record
Create INSTEAD OF INSERT trigger that will do that for you
If you have a table schema like this
CREATE TABLE Table1
([emp_id] varchar(12) primary key, [name] varchar(64))
For the first scenario you can use a query
INSERT INTO Table1 (emp_id, name)
SELECT newid, 'Jhon'
FROM
(
SELECT 'EMP' + CONVERT(VARCHAR(9), COALESCE(REPLACE(MAX(emp_id), 'EMP', ''), 0) + 1) newid
FROM Table1 WITH (TABLOCKX, HOLDLOCK)
) q
Here is SQLFiddle demo
For the second scenario you can a trigger like this
CREATE TRIGGER tg_table1_insert ON Table1
INSTEAD OF INSERT AS
BEGIN
DECLARE #max INT
SET #max =
(SELECT COALESCE(REPLACE(MAX(emp_id), 'EMP', ''), 0)
FROM Table1 WITH (TABLOCKX, HOLDLOCK)
)
INSERT INTO Table1 (emp_id, name)
SELECT 'EMP' + CONVERT(VARCHAR(9), #max + ROW_NUMBER() OVER (ORDER BY (SELECT 1))), name
FROM INSERTED
END
Here is SQLFiddle demo

I am looking to do something similar but don't see an answer to my problem here.
I want a primary Key like "JonesB_01" as this is how we want our job number represented in our production system.
--ID | First_Name | Second_Name | Phone | Etc..
-- Bob Jones 9999-999-999
--ID = "Second_Name"+"F"irst Initial+"_(01-99)"
The number 01-99 has been included to allow for multiple instances of a customer with the same surname and first initial. In our industry it's not unusual for the same customer to have work done on multiple occasions but are not repeat business on an ongoing basis. I expect this convention to last a very long time. If we ever exceed it, then I can simply add a third interger.
I want this to auto populate to keep data entry as simple as possible.
I managed to get a solution to work using Excel formulars and a few helper cells but am new to SQL.
--CellA2 = JonesB_01 (=concatenate(D2+E2))
--CellB2 = "Bob"
--CellC2 = "Jones"
--CellD2 = "JonesB" (=if(B2="","",Concatenate(C2,Left(B2)))
--CellE2 = "_01" (=concatenate("_",Text(F2,"00"))
--CellF2 = "1" (=If(D2="","",Countif($D$2:$D2,D2))
Thanks.

SELECT 'EMP' || TO_CHAR(NVL(MAX(TO_NUMBER(SUBSTR(A.EMP_NO, 4,3))), '000')+1) AS NEW_EMP_NO
FROM
(SELECT 'EMP101' EMP_NO
FROM DUAL
UNION ALL
SELECT 'EMP102' EMP_NO
FROM DUAL
UNION ALL
SELECT 'EMP103' EMP_NO
FROM DUAL
) A

Related

How to sort the GUID or Unique identifer column in SQL

I want to sort the newID column by using ORDER BY, but when I try to order by the id is getting changed each and every time when I execute the query.
I have tired using the CAST operator for converting to VARCHAR and try to sort it. But it is not working.
declare #temp table
(
id int identity(1,1),
newID UNIQUEIDENTIFIER
)
insert into #temp
SELECT NEWID()
insert into #temp
SELECT NEWID()
insert into #temp
SELECT NEWID()
insert into #temp
SELECT NEWID()
select * from #temp
select * from #temp order by cast(newID as varchar(40)) asc
id newID
1 9653de71-34c2-4409-bcee-6809e170e197
2 3f3e7ab8-a516-4dd2-a04b-31feeac8fdea
3 1f1d38b8-3c31-4479-ba48-b71ce8525ea3
4 33f1e2b9-f4c3-4e57-9267-ff729a326318
id newID
3 1f1d38b8-3c31-4479-ba48-b71ce8525ea3
4 33f1e2b9-f4c3-4e57-9267-ff729a326318
2 3f3e7ab8-a516-4dd2-a04b-31feeac8fdea
1 9653de71-34c2-4409-bcee-6809e170e197
The second table also I need to get sorted same like the first table when using ORDER BY statement.
What I think you are trying to do:
I think you want to return your newID values, sorted in ascending order with an incrementing row number in the id column.
What I think you are misunderstanding:
The ID of a row does not need to be in any particular order, it just needs to be unique. If you are using the incrementing integer value of your id column as an identifier elsewhere in your solution, then you do not need to worry about the order. This is important because if you were to insert a new newID value that when sorted fell between two existing newID values, some of the id values would have to change to retain the ordering. This would break any relationships based on the id value.
It is important to note here that the int identity(1,1) value automatically increments (not always by 1) for each row as it is inserted. If you insert your data 'out of order' then the value will also be 'out of order'. I think you are misunderstanding what this functionality is for. In short, it doesn't do what you want it to.
You also can order a uniqueidentifier column as is. You will be getting an error because you have called the column newID, which is a reserved keyword within SQL Server. If you want to keep this name (which I suggest you don't), you will need to reference it within square brackets: order by [newID]. Bear in mind that the 'correct' ordering of a uniqueidentifier is not the same as the alphabetical ordering of the value you see on the screen, much like how the numeric ordering of 1, 2, 3, 10, 11, 12 is different to alphabetically ordering the same values as 1, 10, 11, 12, 2, 3.
How to actually get to your desired output:
If on the off chance you really do just want to get the row number of the newID value that is in your table, you can do this with the row_number windowed function:
declare #temp table(id int identity(1,1)
,newID UNIQUEIDENTIFIER
);
insert into #temp(newID) values
('9653de71-34c2-4409-bcee-6809e170e197')
,('3f3e7ab8-a516-4dd2-a04b-31feeac8fdea')
,('1f1d38b8-3c31-4479-ba48-b71ce8525ea3')
,('33f1e2b9-f4c3-4e57-9267-ff729a326318');
-- Showing the int identity, which increments as new rows are added
select *
from #temp
order by [newID];
-- Using the row_number function to generate the id value at runtime
select row_number() over (order by [newID]) as id
,[newID]
from #temp
order by [newID];
Outputs
Using int identity:
+----+--------------------------------------+
| id | newID |
+----+--------------------------------------+
| 2 | 3F3E7AB8-A516-4DD2-A04B-31FEEAC8FDEA |
| 1 | 9653DE71-34C2-4409-BCEE-6809E170E197 |
| 3 | 1F1D38B8-3C31-4479-BA48-B71CE8525EA3 |
| 4 | 33F1E2B9-F4C3-4E57-9267-FF729A326318 |
+----+--------------------------------------+
and using row_number:
+----+--------------------------------------+
| id | newID |
+----+--------------------------------------+
| 1 | 3F3E7AB8-A516-4DD2-A04B-31FEEAC8FDEA |
| 2 | 9653DE71-34C2-4409-BCEE-6809E170E197 |
| 3 | 1F1D38B8-3C31-4479-BA48-B71CE8525EA3 |
| 4 | 33F1E2B9-F4C3-4E57-9267-FF729A326318 |
+----+--------------------------------------+
NEWSEQUENTIALID() - This will not generate random sequence id for the GUID. We can use this one instead of Newid().
CREATE TABLE Product_A
(
ID uniqueidentifier default NEWSEQUENTIALID(),
productname int
)
Insert Into Product_A(productname) values(1)
Insert Into Product_A(productname) values(2)
Insert Into Product_A(productname) values(3)
Select * from Product_A
Select * from Product_A order by ID
I have used like this, but i want to use the newsequentialid in column wise not has default values in table creation. But it is not possible to use like that. Any suggestion to convert the newid() to newsequentialid because we can sort after the table insertion
Basically in our project we are using the GUID for each transaction, sometimes it can be null also. we are using has unique identifier for the column newid. after the inserting into a table, we want to get the same order has it was inserted in the table. But when we do order by for GUID column, it is randomly sorting. I want the same order as it was inserted in the table

Create a table without knowing its columns in SQL

How can I create a table without knowing in advance how many and what columns it exactly holds?
The idea is that I have a table DATA that has 3 columns : ID, NAME, and VALUE
What I need is a way to get multiple values depending on the value of NAME - I can't do it with simple WHERE or JOIN (because I'll need other values - with other NAME values - later on in my query).
Because of the way this table is constructed I want to PIVOT it in order to transform every distinct value of NAME into a column so it will be easier to get to it in my later search.
What I want now is to somehow save this to a temp table / variable so I can use it later on to join with the result of another query...
So example:
Columns:
CREATE TABLE MainTab
(
id int,
nameMain varchar(max),
notes varchar(max)
);
CREATE TABLE SecondTab
(
id int,
id_mainTab, int,
nameSecond varchar(max),
notes varchar(max)
);
CREATE TABLE DATA
(
id int,
id_second int,
name varchar(max),
value varchar(max)
);
Now some example data from the table DATA:
| id | id_second_int | name | value |
|-------------------------------------------------------|
| 1 | 5550 | number | 111115550 |
| 2 | 6154 | address | 1, First Avenue |
| 3 | 1784 | supervisor | John Smith |
| 4 | 3467 | function | Marketing |
| 5 | 9999 | start_date | 01/01/2000 |
::::
Now imagine that 'name' has A LOT of different values, and in one query I'll need to get a lot of different values depending on the value of 'name'...
That's why I pivot it so that number, address, supervisor, function, start_date, ... become colums.
This I do dynamically because of the amount of possible columns - it would take me a while to write all of them in an 'IN' statement - and I don't want to have to remember to add it manually every time a new 'name' value gets added...
herefore I followed http://sqlhints.com/2014/03/18/dynamic-pivot-in-sql-server/
the thing is know that I want the result of my execute(#query) to get stored in a tempTab / variable. I want to use it later on to join it with mainTab...
It would be nice if I could use #cols (which holds the values of DATA.name) but I can't seem to figure out a way to do this.
ADDITIONALLY:
If I use the not dynamic way (write down all the values manually after 'IN') I still need to create a column called status. Now in this column (so far it's NULL everywhere because that value doesn't exist in my unpivoted table) i want to have 'open' or 'closed', depending on the date (let's say i have start_date and end_date,
CASE end_date
WHEN end_date < GETDATE() THEN pivotTab.status = 'closed'
ELSE pivotTab.status = 'open'
Where can I put this statement? Let's say my main query looks like this:
SELECT * FROM(
(SELECT id_second, name, value, id FROM TABLE_DATA) src
PIVOT (max(value) FOR name IN id, number, address, supervisor, function, start_date, end_date, status) AS pivotTab
JOIN SecondTab ON SecondTab.id = pivotTab.id_second
JOIN MainTab ON MainTab.id = SecondTab.id_mainTab
WHERE pivotTab.status = 'closed';
Well, as far as I can understand - you have some select statement and just need to "dump" its result to some temporary table. In this case you can use select into syntax like:
select .....
into #temp_table
from ....
This will create temporary table according to columns in select statement and populate it with data returned by select datatement.
See MDSN for reference.

Combined SELECT from unnested composite type array and regular column

I have a table my_friends_cards:
id | name | rare_cards_composite[] |
---+---------+------------------------
1 | 'timmy' | { {1923, 'baberuth'}, {1999, 'jeter'}}
2 |'jimmy' | { {1955, 'Joey D'}, {1995, 'juice_head'}}
3 |'bob' | {{2001, 'mo_jeter'}}
I want to make the a request kinda like this:
Select name, (cards.x).player
FROM SELECT UNNEST(base_ball_card) as x
FROM my_friends_cards
WHERE name=ANY(['timmy', 'jimmy'])) as cards
WHERE (cards.x).year > 1990
(I know this doesn't work that there is no 'name' field in the unnested composite array.)
I am getting the feeling that my composite type array column should just be another table, and then I could do a join, but is there anyway around this?
I would expect this result:
[('timmy', 'jeter')
,('jimmy', 'juice_head')]
version: PostgreSQL 9.3.3
Your feeling is correct: a normalized schema with another table instead of the array of composite types would be the superior approach in many respects.
While stuck with your unfortunate design:
Test setup
(You should have provided this.)
CREATE TYPE card AS (year int, cardname text);
CREATE TABLE my_friends_cards (id int, name text, rare_cards_composite card[]);
INSERT INTO my_friends_cards VALUES
(1, 'timmy', '{"(1923,baberuth)","(1999,jeter)"}')
, (2, 'jimmy', '{"(1955,Joey D)","(1995,juice_head)"}')
, (3, 'bob' , '{"(2001,mo_jeter)"}')
;
Query
Requires Postgres 9.3+.
SELECT t.name, c.cardname
FROM my_friends_cards t
, unnest(t.rare_cards_composite) c
WHERE t.name = ANY('{timmy,jimmy}')
AND c.year > 1990;
db<>fiddle here
Old sqlfiddle
Note that the composite type is decomposed in the unnesting.

Factor (string) to Numeric in PostgreSQL

Similar to this, is it possible to convert a String field to Numeric in PostgreSQL. For instance,
create table test (name text);
insert into test (name) values ('amy');
insert into test (name) values ('bob');
insert into test (name) values ('bob');
insert into test (name) values ('celia');
and add a field that is
name | num
-------+-----
amy | 1
bob | 2
bob | 2
celia | 3
The most effective "hash"-function of all is a serial primary key - giving you a unique number like you wished for in the question.
I also deal with duplicates in this demo:
CREATE TEMP TABLE string (
string_id serial PRIMARY KEY
,string text NOT NULL UNIQUE -- no dupes
,ct int NOT NULL DEFAULT 1 -- count instead of dupe rows
);
Then you would enter new strings like this:
(Data-modifying CTE requires PostgreSQL 9.1 or later.)
WITH x AS (SELECT 'abc'::text AS nu)
, y AS (
UPDATE string s
SET ct = ct + 1
FROM x
WHERE s.string = x.nu
RETURNING TRUE
)
INSERT INTO string (string)
SELECT nu
FROM x
WHERE NOT EXISTS (SELECT 1 FROM y);
If the string nu already exists, the count (ct) is increased by 1. If not, a new row is inserted, starting with a count of 1.
The UNIQUE also adds an index on the column string.string automatically, which leads to optimal performance for this query.
Add additional logic (triggers ?) for UPDATE / DELETE to make this bullet-proof - if needed.
Note, there is a super-tiny race condition here, if two concurrent transactions try to add the same string at the same moment in time. To be absolutely sure, you could use SERIALIZABLE transactions. More info and links under this this related question.
Live demo at sqlfiddle.
How 'bout a hash, such as md5, of name?
create table test (name text, hash text);
-- later
update test set hash = md5(name);
If you need to convert that md5 text to a number: Hashing a String to a Numeric Value in PostgresSQL
If they are all single characters, you could do this:
ALTER TABLE test ADD COLUMN num int;
UPDATE test SET num = ascii(name);
Though that would only return the character for the first letter if the string was more than a single character.
The exact case shown in your request can be produced with the dense_rank window function:
regress=# SELECT name, dense_rank() OVER (ORDER BY name) FROM test;
name | dense_rank
-------+------------
amy | 1
bob | 2
bob | 2
celia | 3
(4 rows)
so if you were adding a number for each row, you'd be able to do something like:
ALTER TABLE test ADD COLUMN some_num integer;
WITH gen(gen_name, gen_num) AS
(SELECT name, dense_rank() OVER (ORDER BY name) FROM test GROUP BY name)
UPDATE test SET some_num = gen_num FROM gen WHERE name = gen_name;
ALTER TABLE test ALTER COLUMN some_num SET NOT NULL;
however I think it's much more sensible to use a hash or to assign generated keys. I'm just showing that your example can be achieved.
The biggest problem with this approach is that inserting new data is a pain. It's a ranking (like your example shows) so if you INSERT INTO test (name) VALUES ('billy'); then the ranking changes.

SQL Query for Joining Tables Using A Columns Splitted Strings

Here's what I'm trying to do.
I have these:
Table1:
Name | Surname | Age | Location | ContactPeopleIds
John | Cobaing | 25 | Turkey | 1234,1512,1661, 2366,
Jack | Maltean | 29 | Italy | 6155,2333,1633,
Table2:
ID | Name | LastName | Location
1234 | Meg | Ryan | US
1512 | Jesy | Jade | US
1661 | John | Kradel | US
2366 | Jack | Abdona | Nigeria
TableIWant
Name | Surname | Age | Location | ContactPeopleNames
John | Cobaing | 25 | Turkey | Meg Ryan, Jesy Jade, John Kradel, Jack Abdona
I have found a splitter function called fn_ParseText2Table(data, splitter) that creates a table from the data splitted with splitter char. (Reference here)
For example:
select *
from dbo.fn_ParseText2Table('1234,1512,1661,2366', ',')
function produces:
int_value | num_value | txt_value
null | null | 1234
null | null | 1512
null | null | 1661
null | null | 2366
But I couldn't create a query using this.
I'm not sure to use t-sql or not.
I've tried to use common table expression but couldn't manage that either.
If you can provide multiple solutions, it would be very kind to provide detail about their performance value differences.
ok...
When you suggested that you'd tried a CTE you where heading in the right direction.
What you need to do however is chain 3 CTE's together. Once you have the processing chain you then need to progressively pass things through it like a filter, first splitting the ID's into a column of ints, then joining the ints on table2 to get the names, then recombining those names.
As has been previously mentioned, whoever designed this designed it badly, but assuming your using MS-SQL server and T-SQL the following code will do what you need it to:
DECLARE #tempString AS varchar(max)
SET #tempString =''
;WITH firstCte AS
(
SELECT
CAST('<M>' + REPLACE(contactpeopleids, ',','</M><M>') + '</M>' AS XML) AS Names
FROM
soTable1
-- THIS WHERE CLAUSE MUST MATCH THE FINAL WHERE CLAUSE
WHERE
name = 'John'
AND surname = 'Cobaing'
)
,secondCte AS
(
SELECT
Split.a.value('.','VARCHAR(100)') AS NameIds
FROM
firstCte
CROSS APPLY Names.nodes('/M') Split(a)
)
,thirdCte AS
(
SELECT
t2.name + ' ' + t2.lastname AS theContactName
FROM
secondCte t1
-- NOTE: IF THE IDS YOU EXTRACT FROM TABLE 1 DO NOT HAVE A MATCH IN TABLE 2 YOU WILL GET NO RESULT FOR THAT ID HERE!
-- IF YOU WANT NULL RESULTS CHANGE THIS TO A 'LEFT JOIN'
INNER JOIN
soTable2 t2 ON t1.NameIds = t2.id
)
SELECT
#tempString = #tempString + ',' + theContactName
FROM
thirdCte
;
-- The select substring is used to remove the leading ','
SELECT
name,
surname,
age,
location,
SUBSTRING(#tempString,2,LEN(#tempString)) AS contactpeoplenames
FROM
soTable1
WHERE
name = 'John'
AND surname = 'Cobaing'
It's probably not as elegant as it could be, and for ease of use you might want to wrap it up in a user defined function and pass the firstname and surname of the person to look up into it. If you do it that way, then you can use the function in a regular SQL select query to return rows direct out of table 1 into a view or another table.
The fun part of it all is actually the way in which we trick SQL server into splitting the string. You'll notice that we actually replace the ',' with XML tags, then use the XML processing functions to make SQL server think that we are processing an XML string.
SQL Server has had great routines for doing this kind of task sing the 2005 version, and allows for whole blocks of XML to be serialised and de-serialised to/from a varchar field directly in your db table, by making SQL server think it's dealing with XML it does most of the hard work for us.
**NORMALIZED EXAMPLE OF SELF REFERENCING ONE TO MANY RELATIONSHIP**
Study this example, must apply to yur case, made it fast (and is not fianl code, for example no meassure taken on mysql failure)
Put mysql host username and password..
<?PHP
echo '<pre>';
//mysql connect
mysql_connect('localhost', 'root','');
mysql_select_db("test");
//add some tsting data
addTestingData();
//suppose this come from a user
$_POST['user_id']=1;
//get all contacts of user with id = 1
$sql =
"SELECT `tbl_users`.`user_id`, `user_name`,
`user_surname`,`user_location` from `tbl_users`
LEFT JOIN `tbl_user_contacts`
ON `tbl_users`.`user_id`=`tbl_user_contacts`.`contact`
where `tbl_user_contacts`.`user_id`=".
mysql_real_escape_string($_POST['user_id'])." ";
//get data from mysql
$result = mysql_query($sql ) ;
while($row= mysql_fetch_row($result) )
print_r( $row );
///////////////end////////////////////////////////////////////
function addTestingData()
{
mysql_query("DROP TABLE IF EXISTS `tbl_users`");
mysql_query("
CREATE TABLE `tbl_users` (
`user_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT ,
`user_name` VARCHAR(50) NOT NULL,
`user_surname` VARCHAR(50) NOT NULL,
`user_location` VARCHAR(50) NOT NULL,
`user_age` smallint not null,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
");
for($i=1;$i<21;$i++) {
mysql_query("
insert into `tbl_users` (`user_name`,`user_surname`,`user_location`,
`user_age` ) values
('name{$i}','surname{$i}', 'location{$i}', '{$i}' ) ") ;
}
mysql_query("DROP TABLE IF EXISTS `tbl_user_contacts`");
mysql_query("
CREATE TABLE `tbl_user_contacts` (
`user_id` MEDIUMINT UNSIGNED NOT NULL ,
`contact` MEDIUMINT UNSIGNED NOT NULL ,
`other_field_testing` VARCHAR(30) NOT NULL,
PRIMARY KEY (`user_id`,`contact`),
CONSTRAINT `tbl_contact_fk1` FOREIGN KEY (`user_id`)
REFERENCES `tbl_users` (`user_id`)
ON DELETE CASCADE ,
CONSTRAINT `tbl_contact_fk2` FOREIGN KEY (`contact`)
REFERENCES `tbl_users` (`user_id`)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
");
$tmp=array();//help avoid dupicate entries while testing
for($i=1;$i<99;$i++) {
$contact=rand(1,20);
$user_id=rand(1,20);
if(!in_array($contact.$user_id,$tmp))
{
$tmp[]=$contact.$user_id;
mysql_query("
insert into `tbl_user_contacts` (`user_id`,`contact`,`other_field_testing` )
values ('{$user_id}','{$contact}','optinal-testing') ") ;
}//end of if
}//end of for
}//end of function
?>