I am working with SQL Server 2012 and have table with field defined as:
NVARCHAR(32)
but need to store in it
NVARCHAR(1024)
value and I am not able to change the column length.
I have a procedure that will populate this field. At the same time, it will "do something" if there is field with such value and "do nothing" if there is field with such value.
To solve the issue with the length I've decided to use HashBytes function as follows:
DECLARE #HashThis nvarchar(1024);
SELECT #HashThis = CONVERT(nvarchar(1024),'dslfdkjLK85kldhnv$n000#knf');
SELECT HASHBYTES('SHA1', #HashThis);
GO
I have read that depending on your database collation you could have different hash return for same values. So, is there a way to specify a collation just for the function in order no to depend by database collation.
Also, is there something wrong with my idea?
SELECT HashBytes('SHA1', CONVERT(nvarchar,'вася пупкин' collate Cyrillic_General_CS_AS)) ;
Related
I am using PostgreSQL 11.9
I have a table containing a jsonb column with arbitrary number of key-values. There is a requirement when we perform a search to include all values from this column as well. Searching in jsonb is quite slow so my plan is to create a trigger which will extract all the values from the jsonb column:
select t.* from app.t1, jsonb_each(column_jsonb) as t(k,v)
with something like this. And then insert the values in a newly created column in the same table so I can use this column for faster searches.
My question is what type would be most suitable for storing the keys and then searchin within them. Currently the search looks like this:
CASE
WHEN something IS NOT NULL
THEN EXISTS(SELECT value FROM jsonb_each(column_jsonb) WHERE value::text ILIKE search_term)
END
where the search_term is what the user entered from the front end.
This is not going to be pretty, and normalizing the data model would be better.
You can define a function
CREATE FUNCTION jsonb_values_to_string(
j jsonb,
separator text DEFAULT ','
) RETURNS text LANGUAGE sql IMMUTABLE STRICT
AS 'SELECT string_agg(value->>0, $2) FROM jsonb_each($1)';
Then you can query like
WHERE jsonb_values_to_string(column_jsonb, '|') ILIKE 'search_term'
and you can define a trigram index on the left hand side expression to speed it up.
Make sure that you choose a separator that does not occur in the data or the pattern...
I have a table. I wrote a function in plpgsql that inserts a row into this table:
INSERT INTO simpleTalbe (name,money) values('momo',1000) ;
This table has serial field called id. I want in the function after I insert the row to know the id that the new row received.
I thought to use:
select nextval('serial');
before the insert, is there a better solution?
Use the RETURNING clause. You need to save the result somewhere inside PL/pgSQL - with an appended INTO ..
INSERT INTO simpleTalbe (name,money) values('momo',1000)
RETURNING id
INTO _my_id_variable;
_my_id_variable must have been declared with a matching data type.
Related:
PostgreSQL next value of the sequences?
Depending on what you plan to do with it, there is often a better solution with pure SQL. Examples:
Combining INSERT statements in a data-modifying CTE with a CASE expression
PostgreSQL multi INSERT...RETURNING with multiple columns
select nextval('serial'); would not do what you want; nextval() actually increments the sequence, and then the INSERT would increment it again. (Also, 'serial' is not the name of the sequence your serial column uses.)
#Erwin's answer (INSERT ... RETURNING) is the best answer, as the syntax was introduced specifically for this situation, but you could also do a
SELECT currval('simpletalbe_id_seq') INTO ...
any time after your INSERT to retrieve the current value of the sequence. (Note the sequence name format tablename_columnname_seq for the automatically-defined sequence backing the serial column.)
How can I set a default for column B to be the value in column A?
I know, it is possible in Microsoft SQL Server:
http://www.ideaexcursion.com/2010/04/19/default-column-value-to-identity-of-different-column/
Is it possible in PostgreSQL?
The linked example shows how to intialize one column with the value of the identity column of the same table.
That is possible in Postgres
create table identdefault
(
a serial not null,
b int not null default currval('identdefault_a_seq')
);
serial will create a sequence in the background that is named tablename_column_seq thus we know that the sequence for identdefault.a will be named identdefault_a_seq and we can access the last value through the currval function.
Running:
insert into identdefault default values;
insert into identdefault default values;
insert into identdefault default values;
select *
from identdefault
will output:
a | b
--+--
1 | 1
2 | 2
3 | 3
This seems to only work with Postgres 9.4, when I tried that with 9.3 (on SQLFiddle) I got an error. But in that case it is possible as well - you just can't use the "shortcut" serial but need to create the sequence explicitly:
create sequence identdefault_a_seq;
create table identdefault
(
a int not null default nextval('identdefault_a_seq'),
b int not null default currval('identdefault_a_seq')
);
insert into identdefault default values;
insert into identdefault default values;
insert into identdefault default values;
If you want to have an identical definition as with the serial column, you just need to make the sequence belong to the column:
alter sequence identdefault_a_seq owned by identdefault.a;
SQLFiddle: http://sqlfiddle.com/#!15/0aa34/1
The answer to the much broader question "How can I set a default for column B to be the value in column A?" is unfortunately: no, you can't (see klin's comment)
You can't do it with an actual DEFAULT, but it's trivial with a BEFORE trigger.
CREATE OR REPLACE FUNCTION whatever() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NEW.onecol := NEW.othercol;
RETURN NEW;
END;
$$;
CREATE TRIGGER whatever_tg
BEFORE INSERT ON mytable
FOR EACH ROW EXECUTE PROCEDURE whatever();
"You cannot do that" and postgres don't go well together. There's almost always a way you can do that (whatever "that" turns out to be).
The question is more like: How do you want to do it?
One way, that is nice to DB-Admins would be: Create a before-Trigger, manipulate the new row before it is written.
If your rules to create that new column are very fancy: Turn to one of the embedded languages (like perl).
So: Is it possible? Of course it is.
I wonder if someone could have an idea what is going wrong with this simple query on a hstore column in PostgreSQL 9.2
The queries are runned in pgAdmin
select attributeValue->"CODE_MUN" from shapefile_feature
returns: « attributevalue » column does not exists
when doing:
select * from shapefile_feature;
all the columns are returned including attributeValue, the hstore column
what is the problem?
PostgreSQL distinguish between "identifiers" and 'literal'. Identifiers are schema, table, column's, .. names, literals are others. A attribute in hstore are not SQL identifiers. So you have to pass their names as literals. Operator "->" is only shortcut for function "fetchval(hstore, text)" with possibility be indexed.
select attributeValue->'CODE_MUN' from shapefile_feature
is internally transformed to (don't do this transformation by self!)
select fetchval(attributeValue, 'CODE_MUN') from shapefile_feature
on buggy example in transformed form, you can better understand to error message:
select fetchval(attributeValue, "CODE_MUN") from shapefile_feature
PostgreSQL tries to find column "CODE_MUN" in shapefile_feature, bacause used double quotes means identifiers (in case sensitive notation).
We have an Oracle application that uses a standard pattern to populate surrogate keys. We have a series of extrinsic rows (that have specific values for the surrogate keys) and other rows that have intrinsic values.
We use the following Oracle trigger snippet to determine what to do with the Surrogate key on insert:
IF :NEW.SurrogateKey IS NULL THEN
SELECT SurrogateKey_SEQ.NEXTVAL INTO :NEW.SurrogateKey FROM DUAL;
END IF;
If the supplied surrogate key is null then get a value from the nominated sequence, else pass the supplied surrogate key through to the row.
I can't seem to find an easy way to do this is T-SQL. There are all sorts of approaches, but none of which use the notion of a sequence generator like Oracle and other SQL-92 compliant DBs do.
Anybody know of a really efficient way to do this in SQL Server T-SQL? By the way, we're using SQL Server 2008 if that's any help.
You may want to look at IDENTITY. This gives you a column for which the value will be determined when you insert the row.
This may mean that you have to insert the row, and determine the value afterwards, using SCOPE_IDENTITY().
There is also an article on simulating Oracle Sequences in SQL Server here: http://www.sqlmag.com/Articles/ArticleID/46900/46900.html?Ad=1
Identity is one approach, although it will generate unique identifiers at a per table level.
Another approach is to use unique identifiers, in particualr using NewSequantialID() that ensues the generated id is always bigger than the last. The problem with this approach is you are no longer dealing with integers.
The closest way to emulate the oracle method is to have a separate table with a counter field, and then write a user defined function that queries this field, increments it, and returns the value.
Here is a way to do it using a table to store your last sequence number. The stored proc is very simple, most of the stuff in there is because I'm lazy and don't like surprises should I forget something so...here it is:
----- Create the sequence value table.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[SequenceTbl]
(
[CurrentValue] [bigint]
) ON [PRIMARY]
GO
-----------------Create the stored procedure
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[sp_NextInSequence](#SkipCount BigInt = 1)
AS
BEGIN
BEGIN TRANSACTION
DECLARE #NextInSequence BigInt;
IF NOT EXISTS
(
SELECT
CurrentValue
FROM
SequenceTbl
)
INSERT INTO SequenceTbl (CurrentValue) VALUES (0);
SELECT TOP 1
#NextInSequence = ISNULL(CurrentValue, 0) + 1
FROM
SequenceTbl WITH (HoldLock);
UPDATE SequenceTbl WITH (UPDLOCK)
SET CurrentValue = #NextInSequence + (#SkipCount - 1);
COMMIT TRANSACTION
RETURN #NextInSequence
END;
GO
--------Use the stored procedure in Sql Manager to retrive a test value.
declare #NextInSequence BigInt
exec #NextInSequence = sp_NextInSequence;
--exec #NextInSequence = sp_NextInSequence <skipcount>;
select NextInSequence = #NextInSequence;
-----Show the current table value.
select * from SequenceTbl;
The astute will notice that there is a parameter (optional) for the stored proc. This is to allow the caller to reserve a block of ID's in the instance that the caller has more than one record that needs a unique id - using the SkipCount, the caller need make only a single call for however many IDs are needed.
The entire "IF EXISTS...INSERT INTO..." block can be removed if you remember to insert a record when the table is created. If you also remember to insert that record with a value (your seed value - a number which will never be used as an ID), you can also remove the ISNULL(...) portion of the select and just use CurrentValue + 1.
Now, before anyone makes a comment, please note that I am a software engineer, not a dba! So, any constructive criticism concerning the use of "Top 1", "With (HoldLock)" and "With (UPDLock)" is welcome. I don't know how well this will scale but this works OK for me so far...