create sql function ( Column typeX) - sql

create sql function ( Column typeX)
hi
i have this Table:
autoID | id | name | age | Tel
------------------------------------------
1 | 1 | Frank | 40 | null
2 | 1 | null | 50 | 7834xx
3 | 1 | Alex | null | null
4 | 1 | null | 20 | null
5 | 2 | James | null | 4100xx
6 | 3 | jan | 24 | null
7 | 3 | null | null | 4100xx
my query for select :
SELECT TOP 10
(SELECT top(1) name FROM test1 where id=1 and name is not null order by autoID desc) as name ,(SELECT top(1) age FROM test1 where id=1 and age is not null order by autoID desc) as Age ,(SELECT top(1) Tel
FROM test1
where id=1 and Tel
is not null order by autoID desc) as Telephon FROM [dbo].[test1] group by id
Result:
autoID | id | name | age | Tel
------------------------------------------
1 | 1 | Alex | 20 | 7834xx
I need create function like this:
CREATE FUNCTION TestSchema.MyfunctinX(#ColumnX #ColumnX.type)
RETURNS #ColumnX.type
AS
BEGIN
DECLARE #value #ColumnX.type
SELECT top 1 #value from #ColumnX where #value is not null order by #autoID desc
RETURN #value
END;
GO
for my select query get Short like:
Select Id, MyfunctinX(name) as [Name], MyfunctinX(age) as Age, MyfunctinX(Tel)
as Tel from yourtable Group by Id
Is there a way to do this?

Your structure is not very efficient. You may be better served with an EAV Structure (Entity Attribute Value)
Example
Select A.ID
,Name = [dbo].[MyFunction] (A.ID,'Name')
,Age = [dbo].[MyFunction] (A.ID,'Age')
,Tel = [dbo].[MyFunction] (A.ID,'Tel')
From (Select Distinct ID From YourTable ) A
Returns
ID Name Age Tel
1 Alex 20 7834xx
2 James NULL 4100xx
3 jan 24 4100xx
The UDF
CREATE FUNCTION [dbo].[MyFunction] (#ID int,#Field varchar(100))
Returns varchar(max)
As
Begin
Return (
Select Top 1 with ties Value
From (Select XMLData = cast((Select * from YourTable where ID=#ID For XML RAW) as xml)) A
Cross Apply (
Select autoID = r.value('#autoID','int')
,Item = attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)')
From A.XMLData.nodes('/row') as A(r)
Cross Apply A.r.nodes('./#*') AS B(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('autoID')
and attr.value('local-name(.)','varchar(100)')= #Field
) B
Order By Row_Number() over (Partition By Item Order By autoID Desc)
)
End

Related

Insert on a child table and update FK on parent

I have a parent table with the following structure and data:
---------------------------------------------
| Id | TranslationId | Name |
---------------------------------------------
| 1 | NULL | Image1.jpg |
| 2 | NULL | Image7.jpg |
| 3 | NULL | Picture_Test.png |
---------------------------------------------
And the empty child table which holds the translated images:
-------------------------------------------------------------------------
| Id | De | Fr | En |
-------------------------------------------------------------------------
| | | | |
-------------------------------------------------------------------------
Now I'm looking for a single query statement or at least few queries which I can run without any further programming. Doing this job with scripting or programming would be easy but I have often situations where I need this kind of insert / update. And developing each time a small console app is not feasible.
At the end the two tables should look like this:
---------------------------------------------
| Id | TranslationId | Name |
---------------------------------------------
| 1 | 28 | NULL |
| 2 | 29 | NULL |
| 3 | 30 | NULL |
---------------------------------------------
-------------------------------------------------------------------------
| Id | De | Fr | En |
-------------------------------------------------------------------------
| 28 | Image1.jpg | NULL | NULL |
| 29 | Image7.jpg | NULL | NULL |
| 30 | Picture_Test.png | NULL | NULL |
-------------------------------------------------------------------------
Thank you for any advice.
You could do it something like the below :
INSERT INTO Child
(
Id
,De
,Fr
,En
)
OUTPUT Inserted.Id INTO #Temp
SELECT Id
,De
,Fr
,En
FROM #Values --If you are using a table type to insert into the Child table as a set based approach
;WITH CTE
AS
(
SELECT ROW_NUMBER() OVER(ORDER BY Id) AS Rnk
,Id
FROM #Temp
)
,CTE1 AS
(
SELECT ROW_NUMBER() OVER(ORDER BY Id) AS Rnk
,*
FROM Parent
)
UPDATE cte1
SET TranslationId = cte.Id
FROM CTE1 cte1
JOIN CTE cte ON cte.Rnk = cte1.Rnk
Demo, assuming Name is unique in the first table
create table tab1 (
id int identity
,TranslationId int null
,Name nvarchar(max) null
);
insert tab1 (Name)
values
('Image1.jpg')
,('Image7.jpg')
,('Picture_Test.png')
,(null)
create table tab2 (
id int identity (100,1)
,De nvarchar(max) null
,Fr nvarchar(max) null
,En nvarchar(max) null
);
-- Update them
declare #map table(
name nvarchar(max)
,ref int
);
insert tab2 (de)
output inserted.De, inserted.id
into #map(Name, ref)
select Name
from tab1 src
where Name is not null and not exists (select 1 from tab2 t2 where t2.De = src.Name);
update t1 set TranslationId = ref, Name = null
from tab1 t1
join #map m on t1.Name = m.Name;
select * from tab1;
select * from tab2;
I figured out in the meantime how to do it. Applied on the database, the query looks like this:
DECLARE #Temp TABLE (ImageId INT, Id INT)
MERGE INTO Translation USING
(
SELECT Image.Name AS Name, Image.Id AS ImageId
FROM Candidate
INNER JOIN Candidacy ON Candidate.Id = Candidacy.CandidateId
INNER JOIN Election ON Candidacy.ElectionId = Election.Id
INNER JOIN SmartVoteCandidate ON Candidate.Id = SmartVoteCandidate.CandidateId
INNER JOIN Image ON SmartVoteCandidate.SpiderImageId = Image.Id
WHERE Election.Id = 1575) AS temp ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (De)
VALUES (temp.Name)
OUTPUT temp.ImageId, INSERTED.Id
INTO #Temp (ImageId, Id);
UPDATE Image
SET Image.TranslationId = t.Id, Name = NULL
FROM #Temp t
WHERE Image.Id = t.ImageId
The solution is heavily inspired by
Is it possible to for SQL Output clause to return a column not being inserted?
Using a join in a merge statement

MS SQL Update one table from another table with multiple masterid and different childid

I am looking for a best approach to update 1 table from another table which has common multiple id's.
Below is the Scenario
Table1:
+----------+---------+
| masterid | childid |
+----------+---------+
| 1 | NULL |
+----------+---------+
| 1 | NULL |
+----------+---------+
| 1 | NULL |
+----------+---------+
| 2 | NULL |
+----------+---------+
| 2 | NULL |
+----------+---------+
| 1 | NULL |
+----------+---------+
Table2:
+----------+---------+
| masterid | childid |
+----------+---------+
| 1 | 2 |
+----------+---------+
| 1 | 3 |
+----------+---------+
| 1 | 4 |
+----------+---------+
| 2 | 9 |
+----------+---------+
| 2 | 8 |
+----------+---------+
| 1 | 5 |
+----------+---------+
I wish to update table1 comparing the masterid of table2 and corresponding childid should be be updated.
Solution1: Using ROW_NUMBER()
;
WITH cte
AS (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) rn
FROM #t1),
cte1
AS (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) rn
FROM #t2)
UPDATE t1
SET t1.childid = t2.childid
FROM cte AS t1
INNER JOIN cte1 AS t2
ON t2.masterid = t1.masterid
AND t2.rn = t1.rn
DEMO
How this can be achieved in different ways?
Sample Data
DECLARE #TAble1 AS TABLE (masterid INT , childid INT)
INSERT INTO #Table1
SELECT 1 , NULL UNION ALL
SELECT 1 , NULL UNION ALL
SELECT 1 , NULL UNION ALL
SELECT 2 , NULL UNION ALL
SELECT 2 , NULL UNION ALL
SELECT 1 , NULL
DECLARE #TAble2 AS TABLE (masterid INT , childid INT)
INSERT INTO #TAble2
SELECT 1, 2UNION ALL
SELECT 1, 3UNION ALL
SELECT 1, 4UNION ALL
SELECT 2, 9UNION ALL
SELECT 2, 8UNION ALL
SELECT 1, 5
This Update State is enough to update table Table1 No Need to use Any window function
SELECT * FROM #TAble1
UPDATE T1
SET T1.childid = T2.childid
FROM #TAble1 T1
INNER JOIN #TAble2 T2
ON T1.masterid = T2.masterid
SELECT * FROM #TAble1
Result Before Update
masterid childid
1 NULL
1 NULL
1 NULL
2 NULL
2 NULL
1 NULL
Result After Update
masterid childid
1 2
1 2
1 2
2 9
2 9
1 2

Conditional Group By in SQL

I have the following table that I want to group by type. When there are multiple rows with the same type (e.g., A & B type), I want to preserve the 'value' from the row with the highest rank (i.e., primary > secondary > tertiary..)
rowid | type | rank | value
1 | A | primary | 1
2 | A | secondary | 2
3 | B | secondary | 3
4 | B | tertiary | 4
5 | C | primary | 5
So the resulting table should look like
rowid | type | rank | value
1 | A | primary | 1
3 | B | secondary | 3
5 | C | primary | 5
Any suggestions will be highly appreciated!
p.s., I'm working in MS SQL Server.
You can use row_number(). Here is a simple'ish method:
select t.*
from (select t.*,
row_number() over (partition by type
order by charindex(rank, 'primary,secondary,tertiary')
) as seqnum
from t
) t
where seqnum = 1;
This uses charindex() as a simple method of ordering the ranks.
try this,
;WITH CTE
AS (
SELECT *
,row_number() OVER (
PARTITION BY [type] ORDER BY value
) rn
FROM #t
)
SELECT *
FROM cte
WHERE rn = 1
Another way of doing is with Row_Number and an Order By specifying your rule with CASE.
Schema:
CREATE TABLE #TAB(rowid INT, [type] VARCHAR(1), rankS VARCHAR(50) , value INT)
INSERT INTO #TAB
SELECT 1 , 'A' , 'primary' , 1
UNION ALL
SELECT 2 , 'A' , 'secondary', 2
UNION ALL
SELECT 3 , 'B' , 'secondary' , 3
UNION ALL
SELECT 4 , 'B' , 'tertiary' , 4
UNION ALL
SELECT 5 , 'C' , 'primary' , 5
Now apply rank rule with Row_Number
SELECT * FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY [type] ORDER BY (CASE rankS
WHEN 'primary' THEN 1
WHEN 'secondary' THEN 2
WHEN 'tertiary' THEN 3 END )) AS SNO, * FROM #TAB
)A
WHERE SNO =1
Result:
+-----+-------+------+-----------+-------+
| SNO | rowid | type | rankS | value |
+-----+-------+------+-----------+-------+
| 1 | 1 | A | primary | 1 |
| 1 | 3 | B | secondary | 3 |
| 1 | 5 | C | primary | 5 |
+-----+-------+------+-----------+-------+

Join Vertical & Horizontal table in SQL Server using Pivot

I want to join two tables and combine into one but problem is one table is in Horizontal Format and other in Vertical format.
below is the table structure and join will be on Employeeid:
Table 1 : EmpDetail
ID | CODE | Name
-- |--------| ---
1 | 1008M | ABC
2 | 1039E | XYZ
3 | 1040E | TYS
Table 2 : EmpCustomeDetail
EmpID | FiledName | FieldValue
-- |-------- | ---
1 | FlD1 | temp1
1 | FlD2 | temp2
1 | FlD3 | temp3
2 | FlD1 | temp1
3 | FLD4 | temp6
Desired Output Required :
EmpID | Code | Name | Fld1 | Fld2 | Fld3 | Fld4
-- |---- | ------| --- | ---- |---- |----
1 | 1008M | ABC | temp1 | temp2 | temp3 | null
2 | 1039E | XYZ | temp1 | null | null | null
3 | 1040E | TYS | null | null | null | temp6
I had tried using Pivot Query but it is not giving exact output which i requried
Below is the query so far i have tried
SELECT A.*
FROM (
SELECT
e.Id,
e.code,
e.Fname,
FROM EmpDetail e
LEFT JOIN (
SELECT *
FROM (
SELECT
d.CustomeFieldName
, c.ComboValue
, d.EmployeeId
FROM EmpCustomeDetail d
) src
PIVOT (
MAX(FieldValue)
) src2
) c ON e.Id = c.EmployeeId
) A
Here are two statements:
The first is a simple PIVOT. You can use it, in case you know all Fieldnames (btw: there's a typo in your sample) in advance.
The second is roughly the same statement, but the column names are taken dynamically. This will work with (almost) any count and with different namings.
First a mock-up-test-scenraio
CREATE TABLE DummyEmpDetail (ID INT,CODE VARCHAR(10),Name VARCHAR(100));
INSERT INTO DummyEmpDetail VALUES
(1,'1008M','ABC')
,(2,'1039E','XYZ')
,(3,'1040E','TYS');
CREATE TABLE DummyEmpCustomeDetail (EmpID INT,FiledName VARCHAR(100),FieldValue VARCHAR(100));
INSERT INTO DummyEmpCustomeDetail VALUES
(1,'FlD1','temp1')
,(1,'FlD2','temp2')
,(1,'FlD3','temp3')
,(2,'FlD1','temp1')
,(3,'FLD4','temp6');
--The static PIVOT statement
SELECT p.EmpID
,p.Name
,p.CODE
,p.Fld1
,p.Fld2
,p.Fld3
p,Fld4
FROM
(
SELECT e.CODE,e.Name,ec.*
FROM DummyEmpDetail AS e
INNER JOIN DummyEmpCustomeDetail AS ec ON e.ID=ec.EmpID
) AS tbl
PIVOT
(
MAX(FieldValue) FOR FiledName IN(Fld1,Fld2,Fld3,Fld4)
) AS p;
--The dynamic PIVOT statement
DECLARE #colNames VARCHAR(MAX)=
(
STUFF
(
(
SELECT DISTINCT ',' + QUOTENAME(FiledName) FROM DummyEmpCustomeDetail
FOR XML PATH('')
),1,1,''
)
);
DECLARE #command VARCHAR(MAX)=
'SELECT p.EmpID
,p.Name
,p.CODE
,' + #colNames +
' FROM
(
SELECT e.CODE,e.Name,ec.*
FROM DummyEmpDetail AS e
INNER JOIN DummyEmpCustomeDetail AS ec ON e.ID=ec.EmpID
) AS tbl
PIVOT
(
MAX(FieldValue) FOR FiledName IN(' + #colnames + ')
) AS p;';
EXEC (#command);
GO
DROP TABLE DummyEmpCustomeDetail;
DROP TABLE DummyEmpDetail;
Both lead to the same result...
Try like below. If values of fieldname will not be static then you should use dynamic sql.
SELECT EMPID,
CODE,
NAME,
FLD1,
FLD2,
FLD3,
FLD4
FROM EmpDetail C
JOIN (SELECT A.*
FROM EmpCustomeDetail
PIVOT ( MIN([FIELDVALUE])
FOR [FILEDNAME] IN([FLD1],
[FLD3],
[FLD2],
FLD4) )A)B
ON C.ID = B.[EMPID]
No need for sub-queries.
select e.*,FlD1,FlD2,FlD3,FlD4
from EmpDetail e
left join EmpCustomeDetail
pivot (max(FieldValue) for FiledName in (FlD1,FlD2,FlD3,FlD4)) ecd
on ecd.EmpID = e.ID
+----+-------+------+-------+-------+-------+-------+
| ID | CODE | Name | FlD1 | FlD2 | FlD3 | FlD4 |
+----+-------+------+-------+-------+-------+-------+
| 1 | 1008M | ABC | temp1 | temp2 | temp3 | NULL |
+----+-------+------+-------+-------+-------+-------+
| 2 | 1039E | XYZ | temp1 | NULL | NULL | NULL |
+----+-------+------+-------+-------+-------+-------+
| 3 | 1040E | TYS | NULL | NULL | NULL | temp6 |
+----+-------+------+-------+-------+-------+-------+

Function to detect change in an ordered list

I who like to find a solution in T-SQL that could find a a way to detect a change in a given list or records.
The physical table is like this:
| id |val |
|----|----|
| 1 | A |
|----|----|
| 2 | A |
|----|----|
| 3 | B |
|----|----|
| 4 | B |
|----|----|
| 5 | A |
|----|----|
| 6 | A |
|----|----|
id is a sequencial integer
val is an arbitrary value
I would like to add an calculated field that could somehow denote a change of val
Desired result:
| id |val | segment |
|----|----|---------|
| 1 | A | 1 |
|----|----|---------|
| 2 | A | 1 |
|----|----|---------|
| 3 | B | 2 |
|----|----|---------|
| 4 | B | 2 |
|----|----|---------|
| 5 | A | 3 |
|----|----|---------|
| 6 | A | 3 |
|----|----|---------|
What I'm trying to do is the possibility to group by "segments" like this:
| from_id | to_id | val |
|---------|-------|-----|
| 1 | 2 | A |
| 3 | 4 | B |
| 5 | 6 | A |
|---------|-------|-----|
Assuming SQL Server 2005+
DECLARE #T TABLE (
id INT PRIMARY KEY,
val CHAR(1))
INSERT INTO #T
SELECT 1,'A' UNION ALL SELECT 2,'A' UNION ALL
SELECT 3,'B' UNION ALL SELECT 4,'B' UNION ALL
SELECT 5,'A' UNION ALL SELECT 6,'A'
;WITH cte1 AS(
SELECT
id,
val,
ROW_NUMBER() OVER (ORDER BY id) - ROW_NUMBER() OVER (PARTITION BY val ORDER BY id) AS Grp
FROM #T
),
cte2 AS(
SELECT
id,
val,
MIN(id) OVER (PARTITION BY Grp, val) AS GrpStart
FROM cte1
)
SELECT
id,
val,
DENSE_RANK() OVER (ORDER BY GrpStart) AS segment
FROM cte2
Or the updated requirement is a bit simpler
;WITH cte AS(
SELECT
id,
val,
ROW_NUMBER() OVER (ORDER BY id) - ROW_NUMBER() OVER (PARTITION BY val ORDER BY id) AS Grp
FROM #T
)
SELECT
val,
MIN(id) AS from_id,
MAX(id) AS to_id
FROM cte
GROUP BY Grp, val
ORDER BY from_id
This might have to be modified a bit to work. Also, i never use loops, do this might not be the most efficient way to do it.
Declare #counter int
Set #counter = 1
Declare #seg int
Set #seg = 1
Declare #cur varchar(50)
Set # cur = select val from table where id = 1
While #counter <= select max id from table
Begin
if(#cur == select val from table where id = #counter)
update table set segment = #seg where id = #counter
else
{
set #cur = select val from table where id = #counter
set # seg = #seg + 1
update table set segment = #seg where id = #counter
}
Set #counter = #counter + 1
End
Well thats the general idea anyways...