Implementing this join in SQL Stored procedure - sql

I have 4 tables Position, Employee, Training and Trmatrix.
Table Position
PositionId PosName TrainingId
1 developer 1,2,3
2 Designer 4,5
3 BDA 2,3,6
Table Employee
Employeeid Ename Posid Courseid
1 Alex 1 4
2 Shaun 2 1,2,3
3 Hales 3
Table Training
Trainingid Trainingname
1 JAVA
2 Dot Net
3 PHP
4 Photoshop
5 JQUERY
6 Client Handling
TrMatrix
TrmatId TrID empID
1 1 1
2 2 1
3 3 1
4 4 1
5 4 2
6 5 2
7 1 2
8 2 2
9 2 3
10 3 3
foreign Key relation
trmatrix trId corresponds to the trainingID of the trainingtable.
Employee posid corresponds to the PositionId of the Positiontable.
Employee courseId corresponds to the trainingId of the trianingtable.
BY basic Aim is to get that course/trainingname which is no present in the
EMployee.Courseid column in correspondance to the trmatrix table,
which defines that I have to get the all entries from the trmatrix table for which there is no entry in the employee table Courseid column.
Suppose in case of Alex I have to fetch all the data from the trmatrix table except for course 4 since it is present in the courseid column of the Employee table, so it would return course no 1,2,3 not the no 4.
I am Newbie to the SQL so please help me out with this problem.
Thanks in advance

To start with, you should make PositionTraining and EmployeeCourse tables as well:
PositionTraining
PositionId TrainingId
1 1
1 2
1 3
2 4
2 5
3 2
3 3
3 6
EmployeeCourse
Employeeid Courseid
1 4
2 1
2 2
3 3
and then remove Position.TrainingId and Employee.Courseid.
By doing this you make the data much easier to query.

To get things which are not present in one table from another you can use
WHERE NOT EXISTS (SELECT value FROM OtherTable)
or
WHERE NOT IN (SELECT value FROM OtherTable)
However there is a class of queries called subqueries and these are very useful in this circumstance and a very good article on them is here
http://allenbrowne.com/subquery-01.html
(its written for ms access but the synstax and MS SQL rules are exactly the same so dont be put off)

UDF for spliting out entries
Create function [dbo].[atf_BarListToTable]
(#list ntext)
RETURNS #tbl TABLE (ListPosn int IDENTITY(1, 1) NOT NULL,
SString VARCHAR(1028) NOT NULL) AS
BEGIN
DECLARE #pos int
DECLARE #textpos int
DECLARE #ChunkLength smallint
DECLARE #str nvarchar(4000)
DECLARE #tmpstr nvarchar(4000)
DECLARE #leftover nvarchar(4000)
SET #textpos = 1
SET #leftover = ''
WHILE #textpos <= datalength(#list) / 2
BEGIN
SET #ChunkLength = 4000 - datalength(#leftover) / 2
SET #tmpstr = ltrim(#leftover + substring(#list, #textpos, #ChunkLength))
SET #textpos = #textpos + #ChunkLength
SET #pos = charindex('|', #tmpstr)
WHILE #pos > 0
BEGIN
SET #str = substring(#tmpstr, 1, #pos - 1)
INSERT #tbl (SString) VALUES( #str)
SET #tmpstr = ltrim(substring(#tmpstr, #pos + 1, len(#tmpstr)))
SET #pos = charindex('|', #tmpstr)
END
SET #leftover = #tmpstr
END
IF ltrim(rtrim(#leftover)) <> ''
INSERT #tbl (SString) VALUES(#leftover)
RETURN
END

Related

How to perform calculation in a specific column with multiple conditions

I am looking for a clean solution to perform calculation from a single column with a few conditions and insert it in the same table. My existing solution is to use a while loop with many variable declarations, writing simple query to store value, perform calculation and finally insert it as a new row to the table. However it looks messy and complicated. I am wondering if there is a better solution to it?
Original Table
Week | Indicator | Value
1 A 2
1 B 3
1 D 10
1 E 5
1 X 12
1 Y 6
2 A 4
2 B 5
2 D 7
2 E 3
2 X 4
2 Y 2
...
53
Updated Table
Week | Indicator | Value
1 A 2
1 B 3
1 C 5
1 D 10
1 E 5
1 F 5
1 X 12
1 Y 6
1 Z 2
2 A 4
2 B 5
2 C 9
2 D 7
2 E 3
2 F 4
2 X 4
2 Y 2
2 Z 2
In this example in the updated table, every 3rd row involves different calculation for the same week such that the 3rd row is an addition, 6th row is a subtraction and the 9th row is a division.
The calculation does not restrict to only addition and could include other forms of calculation formulas. I am just using addition as a simple illustration.
Here is an example of my SQL solution:
DECLARE #total_rows int;
SET #total_rows = (SELECT COUNT(*) FROM original_table);
DECLARE #wk varchar(5);
DECLARE #indicator1 char(1);
DECLARE #indicator2 char(1);
SET #indicator1 = 'A';
SET #indicator2 = 'B';
DECLARE #a_value int;
DECLARE #b_value int;
DECLARE #cal_value int;
DECLARE #iteration int
SET #iteration = 1
WHILE #iteration <= #total_rows
BEGIN
IF #iteration <= 53
SET #wk = concat('W',#iteration)
SET #a_value = (SELECT value
FROM original_table
WHERE indicator = #indicator1 and week = #wk);
SET #b_value = (SELECT value
FROM original_table
WHERE indicator = #indicator2 and week = #wk);
SET #cal_value = (#a_value/ NULLIF(#b_value,0)) *1000000;
....
SET #iteration = #iteration + 1
END
Not going to post the entire SQL script as it is quite lengthy but I hope you get the gist of it.
Is this not as simple as INSERT and SUM..?
INSERT INTO dbo.YourTable ([Week],Indicator,[Value])
SELECT YT.[Week],
'C' AS Indicator,
SUM(YT.[Value]) AS [Value]
FROM dbo.YourTable YT
GROUP BY YT.[Week];
DB<>Fiddle

Use PATINDEX to extract a substring in SQL Server?

I have some specific values I want to extract out of a string in SQL Server, but I'm not sure exactly how to get it done with PATINDEX.
Take this string:
declare #Command nvarchar(500) = 'IF dbo.SomeFunctionFn() = 1 BEGIN EXEC SomeStoredProcPR #RowsPerRun=500000, #RowsPerBatch=10000, #NbrDaysToKeepRpt=7 END'
I want to extract out the values of 500000 (for #RowsPerRun), 10000 for #RowsPerBatch and the value of 7 for #NbrDaysToKeepRpt. The values will be of variable length, so I can't guarantee the #RowsPerRun value will be 6 characters.
Is that possible?
DECLARE #Command NVARCHAR(500) =
'IF dbo.SomeFunctionFn() = 1 BEGIN EXEC SomeStoredProcPR #RowsPerRun=500000, #RowsPerBatch=10000, #NbrDaysToKeepRpt=7 END'
SELECT
SearchItem = srch.Txt,
ItemIndex = st.Pos,
ItemLen = t.Ln,
Item = SUBSTRING(pfx.Txt,1,t.Ln)
FROM (VALUES('#RowsPerRun='),('#RowsPerBatch='),('#NbrDaysToKeepRpt=')) AS srch(Txt)
CROSS APPLY (VALUES(CHARINDEX(srch.Txt,#Command),LEN(srch.Txt))) AS st(Pos,Ln)
CROSS APPLY (VALUES(SUBSTRING(#Command, st.Pos+st.Ln, 500))) AS pfx(Txt)
CROSS APPLY (VALUES(PATINDEX('%[^0-9]%',pfx.Txt)-1)) AS t(Ln);
Returns:
SearchItem ItemIndex ItemLen Item
------------------ ----------- ----------- --------
#RowsPerRun= 59 6 500000
#RowsPerBatch= 79 5 10000
#NbrDaysToKeepRpt= 100 1 7
Note that I included a few extra columns to help you understand what's happening.
Update: Against a table
This is how you would apply this logic to a series of values:
DECLARE #sometable TABLE (CommandId INT IDENTITY, Command NVARCHAR(500));
INSERT #sometable (Command)
VALUES
('IF dbo.SomeFunctionFn() = 1 BEGIN EXEC SomeStoredProcPR #RowsPerRun=500000, #RowsPerBatch=10000, #NbrDaysToKeepRpt=7 END'),
('IF dbo.SomeFunctionFn() = 5 BEGIN EXEC SomeStoredProcPR #RowsPerRun=123, #RowsPerBatch=500, #NbrDaysToKeepRpt=20 END'),
('IF dbo.SomeFunctionFn() = 5 BEGIN EXEC XXX #RowsPerRun=43, #RowsPerBatch=1000, #NbrDaysToKeepRpt=120 END'),
('IF dbo.SomeFunctionFn() = 5 BEGIN EXEC abc.yyy #RowsPerRun=43, #RowsPerBatch=1000, #NbrDaysToKeepRpt=120 END');
SELECT t.CommandId, f.SearchItem, f.Item
FROM #sometable AS t
CROSS APPLY
(
SELECT
SearchItem = srch.Txt,
ItemIndex = st.Pos,
ItemLen = t.Ln,
Item = SUBSTRING(pfx.Txt,1,t.Ln)
FROM (VALUES('#RowsPerRun='),('#RowsPerBatch='),('#NbrDaysToKeepRpt=')) AS srch(Txt)
CROSS APPLY (VALUES(CHARINDEX(srch.Txt,t.Command),LEN(srch.Txt))) AS st(Pos,Ln)
CROSS APPLY (VALUES(SUBSTRING(t.Command, st.Pos+st.Ln, 500))) AS pfx(Txt)
CROSS APPLY (VALUES(PATINDEX('%[^0-9]%',pfx.Txt)-1)) AS t(Ln)
) AS f;
Returns:
CommandId SearchItem Item
----------- ------------------ --------
1 #RowsPerRun= 500000
1 #RowsPerBatch= 10000
1 #NbrDaysToKeepRpt= 7
2 #RowsPerRun= 123
2 #RowsPerBatch= 500
2 #NbrDaysToKeepRpt= 20
3 #RowsPerRun= 43
3 #RowsPerBatch= 1000
3 #NbrDaysToKeepRpt= 120
4 #RowsPerRun= 43
4 #RowsPerBatch= 1000
4 #NbrDaysToKeepRpt= 120

How to get parents up to 5 level hierarchy from child id

I created a table with name "content_folder" and inserted values just like this.
cf_id cf_parent_id cf_name
--------------------------------------
1 0 root
2 1 US Blenders
3 2 US Blenders Chil11
4 1 Australian Blenderss
5 1 US Blenders Chil11 -2
40003 1 Child
40206 1 Child 111
40211 2 New
I want to display the folder hierarchy for 5 levels sepereated by '>' in drop drown so I wanted the result in following way
cf_id path
--------------------------------------
1 root
2 US Blenders
3 US Blenders > US Blenders Chil11
4 Australian Blenderss
5 US Blenders Chil11 -2
40003 Child
40206 Child 111
40211 US Blenders > New
I tried by writing following query but output result is not proper.
SELECT t.cf_id,
Group_concat(anc.cf_name ORDER BY anc.cf_name SEPARATOR ' > ') AS path
FROM content_folder AS t
JOIN content_folder AS anc
ON t.cf_name LIKE Concat(anc.cf_name, '%')
GROUP BY t.cf_id;
Can you please suggest me better solution. I am working on MySQL 5.7 so I think CTEs are not supported in this version.
I had done something like this with my database. Here is how i did it, i changed a few things and added the two update statements at the end to get your desired output, but i think you will get the gist of whats going on:
DECLARE #temp TABLE
(
id INT,
parent_id INT,
name NVARCHAR(100),
path NVARCHAR(MAX)
)
INSERT INTO #temp (id,parent_id,name) VALUES
(1,0,'root'),
(2,1,'US Blenders'),
(3,2,'US Blenders Chil11'),
(4,1,'Australian Blenderss'),
(5,1,'US Blenders Chil11 -2'),
(40003,1,'Child'),
(40206,1,'Child 111'),
(40211,2,'New')
DECLARE #counter TABLE ( id INT )
DECLARE #current_id INT;
DECLARE #loc_id INT;
INSERT INTO #counter SELECT id FROM #temp
SELECT * FROM #temp
WHILE (SELECT COUNT(*) FROM #counter) > 0
BEGIN
SET #current_id = (SELECT TOP 1 id FROM #counter)
SET #loc_id = (SELECT parent_id FROM #temp WHERE id = #current_id)
WHILE #loc_id IS NOT NULL AND #loc_id != 0
BEGIN
UPDATE #temp
SET path = ' > ' + (SELECT name FROM #temp WHERE id = #loc_id) + CASE WHEN path IS NULL THEN '' ELSE path END
WHERE id = #current_id
SET #loc_id = (SELECT parent_id FROM #temp WHERE id = #loc_id)
END
DELETE FROM #counter
WHERE id = #current_id
END
UPDATE #temp
SET path = REPLACE(REPLACE(path,' > root',''),' > ','')
UPDATE #temp
SET path = CASE WHEN LEN(path) > 0 THEN path + ' > ' + name ELSE name END
SELECT * FROM #temp
OUTPUT:
id parent_id name path
1 0 root root
2 1 US Blenders US Blenders
3 2 US Blenders Chil11 US Blenders > US Blenders Chil11
4 1 Australian Blenderss Australian Blenderss
5 1 US Blenders Chil11 -2 US Blenders Chil11 -2
40003 1 Child Child
40206 1 Child 111 Child 111
40211 2 New US Blenders > New

SQL - How to select a column alias from another table?

I have a table in SQL with data and another table that holds the alias for that column. It is used for translation purposes.
I was wondering how can I do a select on those columns but retrieve the alias from another table?
This is the table that holds the real column names:
ID PageID ColName Order Type Width IsDeleted
1 7 CustType 2 NULL NULL 0
2 7 Description 3 NULL NULL 0
3 7 ApplyVAT 4 NULL NULL 0
4 7 ProduceInvoices 5 NULL NULL 0
5 7 PurchaseSale 6 NULL NULL 0
6 7 TermsDays 7 NULL NULL 0
7 7 DateTimeLastUpdated 8 NULL NULL 0
This is the table that holds the alias (text):
ID ColID UserID Text Order Enabled?
50 22 1 id 1 1
51 1 1 CustTypes 2 1
52 2 1 Description 3 1
53 3 1 ApplyVAT NULL 0
54 4 1 ProduceInvoices NULL 0
55 5 1 PurchaseSale NULL 0
56 6 1 TermsDays NULL 0
57 7 1 DateTimeLastUpdated NULL 0
I believe you will need to use dynamic sql to do this, e.g.:
DECLARE #Sql NVARCHAR(MAX);
SELECT TOP 1 #Sql = 'SELECT dt.ID as ' + at.IDAlias + ', dt.Town as ' + at.TownAlias
+ ' FROM DataTable dt'
FROM AliasTable at
WHERE at.LanguageID = 2;
EXEC(#Sql)
Given the example of Data Table
CREATE TABLE DataTable
(
ID INT,
Town NVARCHAR(50)
);
And a table holding language - dependent aliases for the columns in the above:
CREATE TABLE AliasTable
(
LanguageId INT,
IDAlias NVARCHAR(100),
TownAlias NVARCHAR(100)
);
SqlFiddle here
One of the (many) caveats with dynamic Sql is you will need to ensure that the alias data is validated against Sql Injectin attacks.

Using a Cursor to Update Rows in Single Table

I am seeking guidance using MS SQL cursor or SQL while loop to merge (add, update, and set inactive) rows using single table based on criteria below resulting in seeking the final dataset:
TABLE VIEW (SELECT * FROM TABLE WHERE ENTITY = 123 (#ID)
ENTITY ENTITY_TYPE VALUE STATUS_TYPE
123 1 1 1
123 1 4 1
123 1 9 1
TABLE VIEW (SELECT * FROM TABLE WHERE ENTITY = 456 (#OverrideID)
ENTITY ENTITY_TYPE VALUE STATUS_TYPE
456 1 1 1
456 1 5 1
Final Data Set below:
ENTITY&nbsp&nbsp&nbspTYPE VALUE STATUS_TYPE
123 1 1 3
123 1 4 3
123 1 9 3
456 1 1 1
456 1 4 1
456 1 9 1
456 1 1 1
456 1 5 1
-- Check and compare each row
-- IF #OverrideID = #ID (MATCH)
----- Set #ID to inactive keeping copy
-- If #OverrideID != #ID (NOT MATCH)
----- Insert #ID data with #OverrideID (COPY)
----- Set #ID to inactive.
I began writing the following and need help. For reference, #ID = 123 and #OverrideID = 456
DECLARE #ENTITY BIGINT, #ENTITY_TYPE BIGINT, #VALUE BIGINT, #E1 BIGINT, #T1 BIGINT, #V1 BIGINT
DECLARE type_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT * FROM TypeValue WHERE ENTITY = #ID
SET NOCOUNT OFF
OPEN type_cursor
FETCH NEXT FROM type_cursor INTO #OverrideID, #ID, #ENTITY, #ENTITY_TYPE, #VALUE
WHILE (##FETCH_STATUS = 0)
BEGIN
SELECT #ENTITY, #ENTITY_TYPE, #VALUE
IF #VALUE IS NOT NULL
BEGIN
SELECT #T1 = #ENTITY_TYPE, #V1 = #VALUE
END
ELSE
BEGIN
UPDATE TypeValue
SET ENTITY = #OverrideID, ENTITY_TYPE = #T1, VALUE = #V1 WHERE ENTITY = #ID
END
FETCH NEXT FROM type_cursor INTO #OverrideID, #ID, #ENTITY, #ENTITY_TYPE, #VALUE
END
CLOSE type_cursor
DEALLOCATE type_cursor
SET NOCOUNT OFF
That final result set looks like
select entity,entity_type,value,3 status_type
from [table]
where entity = #id
union all
select #override,entity_type,value,status_type
from [table]
where entity = #id
union all
select entity,entity_type,value,status_type
from [table]
where entity = #override