SQL matching with rest of the columns if any of the where condition parameter is missing - sql

Here is my table structure:
ID cid Name Course Interval
1 1 KB Y 2
2 1 TB Y 3
3 2 BK N 1
I need to write a query which returns all rows with matching condition.
if at all any of the parameter is null or not provided then i need to return all of the matching rows.
In my select query if my parameters are (cid ==1 and Name== null and course ==Y or (cid ==1 and course ==Y ) then I need to return rows with id
1 and 2.
What exactly I need is this:
If I get all the matching record then i can take interval corresponding to the record. Else I need to take average interval of the matching record

Is this what you are after? Its a variable number or arguments - in my case the parameters are explicitly defined, yours may be passed in from a stored proc etc.
-- Create the table
create table #t(ID int, cid int, Name char(2), Course char(1), Interval int)
insert #t values (1,1,'KB','Y',2)
,(2,1,'TB','Y',3)
,(3,2,'BK','N',1)
-- Declare the arguments
declare #cid int
declare #name char(2)
declare #course char(1)
-- Set one or more arguments
set #cid=1
set #name=null
set #course='Y'
select AVG(convert(decimal(5,2),interval)) from #t
where isnull(#cid,cid)=cid
and isnull(#name,name)=name
and isnull(#course,course)=course

Related

SQL Server / T-SQL: Return a single value from a constructed table

I am writing an inline function, which accepts a fixed number of parameters (9 in the code example), arranges those parameters into a constructed table with 2 columns and 4 rows, sorts the table, and then returns a single value from said table (the value in column 1, and in whichever row 1-4 the calling statement requested).
I've got most of it done.
But I need to know how to select a specific column and row index at the end.
You may be asking why I need an inline function for this, and the answer is because a) the sorting is crucial and b) this is being used in a computed column in a memory-optimized table, where sub-queries are not allowed, etc.
Here's the code I have so far:
CREATE FUNCTION [dbo].[SelectOrderedInputValue]
(
-- 1-based row index of the desired return value
#RequestedRowIndex INT,
#InputValue1 INT,
#InputValue2 INT,
#InputValue3 INT,
#InputValue4 INT,
#InputRank1 INT,
#InputRank2 INT,
#InputRank3 INT,
#InputRank4 INT
)
RETURNS TABLE AS RETURN
(
/* Places the parameters into a table, and sorts by rank.
I need to figure out how to specify a row and column,
so I can return the single requested value from this table.
In this case, I need to return the value in column #1 (alias InputValues)
and row # #RequestedRowIndex*/
SELECT TOP 4 InputValues FROM
(VALUES
([InputValue1], [InputRank1]),
([InputValue2], [InputRank2]),
([InputValue3], [InputRank3]),
([InputValue4], [InputRank4])
) AS Inputs(InputValues, InputRanks) ORDER BY InputRanks ASC
)
There is no need to return a table for a single value. Just use a scalar function:
CREATE FUNCTION [dbo].[SelectOrderedInputValue] (
-- 1-based row index of the desired return value
#RequestedRowIndex INT,
#InputValue1 INT,
#InputValue2 INT,
#InputValue3 INT,
#InputValue4 INT,
#InputRank1 INT,
#InputRank2 INT,
#InputRank3 INT,
#InputRank4 INT
) RETURNS INT AS
BEGIN
/* Places the parameters into a table, and sorts by rank.
I need to figure out how to specify a row and column,
so I can return the single requested value from this table.
In this case, I need to return the value in column #1 (alias InputValues)
and row # #RequestedRowIndex*/
DECLARE #retval INT;
SET #retval = (SELECT TOP 4 *
FROM (VALUES ([InputValue1], [InputRank1]),
([InputValue2], [InputRank2]),
([InputValue3], [InputRank3]),
([InputValue4], [InputRank4])
) Inputs(InputValues, InputRanks)
ORDER BY InputRanks ASC
OFFSET #RequestedRowIndex-1 FETCH FIRST 1 ROW ONLY
);
RETURN #retval;
END;
Try with OFFSET FETCH
DECLARE #RequestedRowIndex INT=4
SELECT * FROM (
SELECT TOP 4 * FROM
(VALUES
(10, 11),
(20, 21),
(30, 31),
(40, 41)
) AS Inputs(InputValues, InputRanks)
ORDER BY InputRanks ASC )A
ORDER BY InputRanks ASC
OFFSET #RequestedRowIndex-1 ROWS FETCH NEXT 1 ROWS ONLY

Trouble updating log with triggers using SQL Server

I am trying to create a trigger with a higher difficulty that would let me create a log after updating rows in alumns table
| Alumn_ID | Name | Courses | Favourite_Course
1 Peter 5 Math
And this would be the result if for example someone updated the number of courses from 5 to 6.
| Log_ID | Alumn_ID | Note | NoteID | Change_Date | Last_Change_Date
1 1 Fields were Updated Note 1 2018-04-23 00:00:00.000 2018-03-23 00:00:00.000
Here is my current trigger
ALTER TRIGGER [LOG]
ON ALUMNS
AFTER UPDATE
AS
BEGIN
DECLARE #Note VARCHAR(50)
DECLARE #Alumn_ID varchar;
SELECT #Alumn_ID= INSERTED.Alumn_ID FROM INSERTED
SET #Note = 'Fields were updated'
INSERT INTO Alumn_Log (Log_ID, Alumn_ID, Note, NoteID, Change_Date)
SELECT Log_ID, i.Alumn_ID, #Note, NoteID, GETDATE(); FROM INSERTED i
END
My problem is:
How do i create the Log ID and the Note ID that i can't take from INSERTED i?
My second problem is, how do i insert the current date? when i try to execute the query it tells me that i can't use that variable in INSERTS.
My third problem, is how can i put the "Last change date"?
Fourth, is there a way to type an specific Note for example if only the name was changed it should say "Name was changed"?
Finally, The Note ID would be Varchar not identity and every note ID needs to be different
This is the current and only error that's preventing me from running the Query:
This is what i get Msg 273, level 16, state 1, procedure Log_Trigger, line 19 [Batch Start Line 0] me time stamp Use INSERT with a list of columns to exclude the timestamp column or insert DEFAULT in the timestamp column.
Here is how I would approach it.
How do i create the Log ID and the Note ID that i can't take from
INSERTED i?
The Log Id can be an AutoIdentity column. An INT column with IDENTITY INSERT.
The Note ID can be an Auto Incremented Computed column (shown in the code below). You would probably need to introduce a new column that serves as a prefix.
My second problem is, how do i insert the current date? when i try to
execute the query it tells me that i can't use that variable in
INSERTS.
GETDATE()?
My third problem, is how can i put the "Last change date"?
You can have a join with INSERTED and get the value from the log from a previous row. Shown in the code.
Fourth, is there a way to type an specific Note for example if only
the name was changed it should say "Name was changed"?
That would depend on finding the nature of the update on which column. This is more of a business question than a technical question.
Finally, The Note ID would be Varchar not identity and every note ID needs to be different
Now, the code (the entire schema)
CREATE TABLE LOG(
Log_ID INT IDENTITY(1, 1) NOT NULL,
Alumn_ID INT,
NOTE VARCHAR(200),
PREFIX VARCHAR(30),
NOTEID AS([PREFIX] + RIGHT('0000000' + CAST(Log_ID AS VARCHAR(7)), 7)) PERSISTED,
CHANGEDATE DATETIME,
LASTCHANGEDATE DATETIME
);
CREATE TABLE ALUMN(
Alumn_ID INT,
NAME VARCHAR(50),
COURSES INT,
FAVORITE_COURSE VARCHAR(50)
);
CREATE TRIGGER[trg_LOG]
ON ALUMN
AFTER UPDATE
AS
BEGIN
DECLARE #Note VARCHAR(50)
--DECLARE #Alumn_ID VARCHAR(50)
DECLARE #Lastchange DATETIME
--SELECT #Alumn_ID = INSERTED.Alumn_ID FROM INSERTED
SET #Note = 'Fields were updated'
SELECT #Lastchange = CHANGEDATE FROM LOG l
INNER JOIN INSERTED i ON l.Alumn_ID = i.Alumn_ID
--INNER JOIN ALUMN
INSERT INTO LOG(Alumn_ID, Note, Prefix, CHANGEDATE, LASTCHANGEDATE)
SELECT i.Alumn_ID, #Note, 'AUP', GETDATE(), #Lastchange FROM INSERTED i
END
how do i insert the current date? when i try to execute the query it
tells me that i can't use that variable in INSERTS.
SELECT Log_ID, i.Alumn_ID, #Note, NoteID, GETDATE(); FROM INSERTED i
Take the semi-colon out of the line above.
SELECT Log_ID, i.Alumn_ID, #Note, NoteID, GETDATE() FROM INSERTED i

How to populate a column's value based on two other column's values

I have a table like this:
| DEL_ID | CP_ID | ID | QUANTITY | FP_ID | RESULT |
I need to populate the result column by using the Quantity and FP_ID columns by concatenating the FP_ID value to itself, and do so as many times as the value of QUANTITY.
So RESULT = FP_ID concatenated to itself QUANTITY times.
If QUANTITY is 3 and FP_ID is 23 then the result should be 232323
I need to do an insert that inserts this result for each row based on this logic.
How do I do this in SQL?
It should be computed column....
I mean I should be declared as it is based on other two columns
if you want to add after creation of table
ALTER TABLE tblResults
ADD Final_Result as replicate(FP_ID ,Quantity)
else
while creation
Create table tblResults
(.......ur columns..... , Final_Result as replicate(FP_ID ,Quantity))
you no need to give data for this column, It will automatically loaded when the data is loaded into table "tblResults"
NOTE: If any value is null then the Final_Result value will also be NULL
I have edited my answer as below:
Please execute the below query to create a Function:
CREATE FUNCTION [dbo].[ConcatenateString]
(
#FP_ID INT,
#QUANTITY INT
)
RETURNS
NVARCHAR(MAX)
AS
BEGIN
DECLARE #ConcatenatedString NVARCHAR(MAX), #ConvertedFP_ID NVARCHAR(100)
SET #ConcatenatedString =''
SET #ConvertedFP_ID = CONVERT(varchar(100),#FP_ID)
WHILE #QUANTITY >= 1
BEGIN
SELECT #ConcatenatedString = #ConcatenatedString+#ConvertedFP_ID
SET #QUANTITY = #QUANTITY - 1
END
RETURN #ConcatenatedString
END
GO
And you can call the Function in the INSERT script:
INSERT INTO tblResults(DEL_ID,CP_ID,ID,QUANTITY,FP_ID,RESULT)
VALUES(1,2,3,4,5,(SELECT dbo.ConcatenateString(4,5) AS ConcatnatedValue))

How to create conditional unique constraint

Having a table:Table1 in which a column Code accepts nullables values how can we insure that values are unique for non nullable values except for codes that start with 'A' which can be duplicated maximum twice?
Table1
Id | Code
----------
1 | NULL --[ok]
2 | A123 --[ok]
3 | A123 --[ok]
4 | B100 --[ok]
5 | C200 --[ok]
6 | B100 --[not ok already used]
7 | NULL --[ok]
What i have tried is creating an indexed view, the solution work fine for NULL values but not for the second case i mentioned (skipped actualy)
Create view v_Table_unq with schemabinding as(
select code from
dbo.Table1
where code is not null and code not like 'A%'
)
go
create unique clustered index unq_code on v_Table_unq(code)
Thanks for help
Table Creation
CREATE TABLE CheckConstraint
(
Name VARCHAR(50),
)
GO
Function Creation
create FUNCTION CheckDuplicateWithA() RETURNS INT AS BEGIN
DECLARE #ret INT =0 ;
SELECT #ret = IsNull(COUNT(Name), 0) FROM CheckConstraint WHERE Name like '[A]%' group by Name having COUNT(name) >= 1;
RETURN IsNUll(#ret, 0);
END;
GO
create FUNCTION CheckDuplicateOtherThenA() RETURNS INT AS BEGIN
DECLARE #ret INT =0 ;
SELECT #ret = IsNull(COUNT(Name), 0) FROM CheckConstraint WHERE Name not like '[A]%' group by Name having COUNT(name) >= 1;
RETURN IsNUll(#ret, 0);
END;
GO
Constraints
alter TABLE CheckConstraint
add CONSTRAINT CheckDuplicateContraintWithA CHECK (NOT (dbo.CheckDuplicateWithA() > 2));
go
alter TABLE CheckConstraint
add CONSTRAINT CheckDuplicateConmstraintOtherThenA CHECK (NOT (dbo.CheckDuplicateOtherThenA() > 1));
go
Result Set
insert into CheckConstraint(Name)Values('b') -- Passed
insert into CheckConstraint(Name)Values('b') -- Failed
insert into CheckConstraint(Name)Values('a') -- Passed
insert into CheckConstraint(Name)Values('a') -- Passed
insert into CheckConstraint(Name)Values('a') -- Failed
Why would you want a unique contraint? Why cant add this logic in the proc which inserts the data in the table?If you do not have a single point of insertion/updation etc?Why cant put it in instead of or after trigger?That would be much better as you can handle it well and could return proper errror messages.This will have less overhead than having a index view which will add to overhead.If you need unique constraint for the records which doesnt start with 'A' then you can have a persisted column and have a unique constraint on that.
Off course you will have overhead of having persisted computed column with index..But if you just need unique contsraint you can use that.For values which starts with 'A' this could be a null value.

Selecting a user-defined scalar function that takes as a parameter another field

I have a table a with a list of id's, and a user-defined function foo(id) that takes the id and returns a VARCHAR(20).
What I am trying to do is:
SELECT
id,
foo(id) AS 'text field'
FROM a
However, instead of calling the function for each ID number, like I desired, the text comes back the same for every row. I have tested the foo() function manually with the returned ID's and it does not have that problem, so I realize I must not understand something about the evaluation of the query.
This worked for me. I'm not sure what your saying you get.
CREATE TABLE [dbo].[a](
[id] [int] NULL
)
insert into a select 1
insert into a select 2
insert into a select 4
insert into a select 5
CREATE FUNCTION foo(#id int) RETURNS varchar(20)
AS
BEGIN
DECLARE #ResultVar varchar(20)
SELECT #ResultVar = '# - ' + CAST(#id as varchar(20))
RETURN #ResultVar
END
select id, dbo.foo(id) AS 'text field' from a
returns
id text field
----------- --------------------
1 # - 1
2 # - 2
4 # - 4
5 # - 5
6 # - 6
If the output of the function is functionally dependent on the input, then it will be called for each row, as you expect. If you see a different result, it means that you do not pass the correct input or your function output is not dependent on its input.