How to transpose rows to columns in SQL Server - sql-server

I have a fact table as below:
Table Types
TypeId | Name
1 x
2 y
3 z
Table Period:
PeriodId | Date
1 2014-01-31
2 2015-01-31
RowNumber | Value | TypeId | Identifier | PeriodId
1 12 1 cc1 1
2 10 2 cc1 2
3 17 3 cc1 1
.. 30 ... ... ..
.. 60 1 cc2 1
23 2 cc2 2
From these tables I am trying to create a single flatten table as below:
Identifier | periodId | x | y | z
cc1 1 12 10 17
cc1 2 .. .. ..
cc2 1 .. .. ..
How can I query to get the data in the above format?

INSERT INTO NewTable
SELECT Identifier, PeriodId,
(SELECT Value FROM [Values] v2 WHERE v2.Identifier = v1.Identifier AND v2.PeriodId = v1.PeriodId AND v2.TypeId = 1) AS 'x',
(SELECT Value FROM [Values] v2 WHERE v2.Identifier = v1.Identifier AND v2.PeriodId = v1.PeriodId AND v2.TypeId = 2) AS 'y',
(SELECT Value FROM [Values] v2 WHERE v2.Identifier = v1.Identifier AND v2.PeriodId = v1.PeriodId AND v2.TypeId = 3) AS 'z'
FROM [Values] v1
GROUP BY Identifier, PeriodId
Is this what you need?

Related

SQL LITE difference of 2 date time elements in same column

I am new in SQL lite, I have a datetime column in this format:
id
datetime
1
2020-12-26 19:08:49
2
2020-12-26 19:08:50
3
2020-12-26 19:08:51
4
2020-12-26 19:08:51
5
2020-12-26 19:09:07
6
2020-12-26 19:11:45
7
2020-12-26 19:52:49
8
2020-12-26 19:52:50
How can i compute the difference between the first element with the 2nd
the 3rd with 4th element ??
Use window function FIRST_VALUE() to get the datetime of the the 1st row and with the function strftime() you can calculate the difference of each row:
SELECT *
FROM (
SELECT id,
strftime('%s', datetime) - strftime('%s', FIRST_VALUE(datetime) OVER (ORDER BY id)) AS diff_in_secs
FROM tablename
)
WHERE id > 1
Or with a self join:
SELECT t.id,
strftime('%s', t.datetime) - strftime('%s', m.datetime)
FROM tablename t
INNER JOIN (SELECT * FROM tablename ORDER BY id LIMIT 1) m
ON m.id < t.id
I used id and datetime for the column names and tablename for the table's name, you can change them to the actual ones.
See the demo.
Results:
> id | diff_in_secs
> -: | -----------:
> 2 | 1
> 3 | 2
> 4 | 2
> 5 | 18
> 6 | 176
> 7 | 2640
> 8 | 2641

Is it possible to duplicate a row based on a column value, and change another column value?

I have a table with an INT column of time in seconds, another column with the type of that record, and a foreign ID.
I want to select the same row repeated everytime it's seconds row is greater than X, and in the first row it keeps X seconds with type 1, and in the second row it shows the seconds left (seconds - X, up to X) with type 2. The same with Y. No more than 3 rows. So row 1 is up to x, row 2 up to Y and row 3 is Y+1 and beyond
Eg:
X is 5. Y is 9.
I want this:
| id | type | seconds |
|----|------|---------|
| 1 | 10 | 19 |
| 2 | 10 | 12 |
| 3 | 10 | 7 |
to become this:
| id | type | seconds |
|----|------|---------|
| 1 | 1 | 5 |
| 1 | 2 | 4 |
| 1 | 3 | 10 |
| 2 | 1 | 5 |
| 2 | 2 | 4 |
| 2 | 3 | 3 |
| 3 | 1 | 5 |
| 3 | 2 | 2 |
Is that possible?
I've seen solutions to this (syntax-) exclusively for oracle.
But how do I do this in SQL Server?
Edit: Only for this type id (10 in the example), letting others unchanged.
This is not very pretty, but does the trick. To duplicate rows, we use tally tables. In this case I used a hard coded one with only 3 rows. Then made the calculations based on it. There might be a simpler way to code it, but I'm not on my best today.
--Creating sample data
CREATE TABLE SampleData(
id int,
[type] int,
seconds int
);
INSERT INTO SampleData
VALUES
( 1, 10, 19),
( 2, 10, 12),
( 3, 10, 7),
( 4, 10, 5),
( 5, 10, 3);
GO
--Actual solution
DECLARE #X int = 5;
WITH CTE AS(
SELECT id,
CASE WHEN [type] = 10 THEN n ELSE [type] END AS [type],
CASE WHEN [type] <> 10 THEN seconds
WHEN n = 1 AND seconds > #X THEN #X
WHEN n = 1 AND seconds <= #X THEN seconds
WHEN n = 2 AND seconds - #X >= #X THEN #X-1
WHEN n = 2 AND seconds - #X > 0 THEN seconds - #X
WHEN n = 3 AND seconds > #X*2-1 THEN seconds - (#X*2-1)
END AS seconds
FROM SampleData
CROSS JOIN( VALUES(1),(2),(3))AS x(n)
WHERE [type] = 10 OR n = 1
)
SELECT *
FROM CTE
WHERE seconds IS NOT NULL;
For this type of problem I like to use a recursive CTE. Borrowed the extra test cases from #Luis Cazares. Also added test cases for type 15 you mentioned in comments. In the solution below you can swap out values of X and Y and it will calculate accordingly.
CREATE TABLE #TestData
(
id INT
, type INT
, seconds int
);
INSERT INTO #TestData VALUES
(1, 10, 19)
, (2, 10, 12)
, (3, 10, 7)
, (4, 10, 5)
, (5, 10, 3)
, (6, 15, 54)
, (7, 15, 8);
DECLARE
#X INT = 5
, #Y INT = 9;
SET #Y = #Y - #X;
WITH CTE AS
(
SELECT id, CASE WHEN type = 10 THEN 0 ELSE type END AS type, seconds, seconds AS ov
FROM #TestData
UNION ALL
SELECT
id
, type + 1
, CASE
WHEN type = 0 THEN seconds - #X
WHEN type = 1 THEN seconds - #Y
WHEN type = 2 THEN seconds
END
, ov
FROM CTE
WHERE seconds > 0
)
SELECT
id
, type
, CASE
WHEN type = 1 AND seconds > 0 THEN #X
WHEN type = 2 AND seconds > 0 THEN #Y
WHEN type = 2 AND seconds < 0 THEN ov - #X
WHEN type = 3 AND seconds > 0 THEN seconds
ELSE ov
END
FROM CTE
WHERE type <> 0 AND CTE.seconds IS NOT NULL
ORDER BY id, type
DROP TABLE #TestData;

SQL Server - build up query using table valued function

I have the following tables structure:
tblMapping
map_id | name | parent_id
1 A 0
2 B 1
3 C 2
4 D 2
5 E 4
tblEditableContent
id | map_id | desc_id | isExcluded
1 1 0 0
2 4 0 1
In this table, tblEditableContent, desc_id=0 means that all elements below the given map_id (all its children) are allowed to be displayed and desc_id=1 means that the node itself is selected.
I also have a table valued function (mapping) that will build up the node for a given map_id, similar to the result below:
for select * from dbo.mapping(1)
map_id | name
1 A
2 B
3 C
4 D
5 E
for select * from dbo.mapping(4)
map_id | name
4 D
5 E
Now, for the task and requirement.
I try to create the following result:
map_id | name | desc_id | isExcluded
1 A 0 0
2 B 0 0
3 C 0 0
In the same time, if we change the value of desc_id in tblEditableContent from 0 to 1, then the output should be:
map_id | name | desc_id | isExcluded
1 A 0 0
2 B 0 0
3 C 0 0
5 E 0 0
And here is what I have tried out until now:
select ca.map_id, ca.name, ec.desc_id, ec.isExcluded from tblEditableContent ec
CROSS APPLY (
Select * From dbo.mapping(ec.map_id)
) ca
where ec.isExcluded = 0
order by ca.map_id
You can try a query like below:
select
ec.*
from tblEditableContent ec
join
(
select
ca.map_id as map_id,
MAX(ec.isExcluded) as isExcluded
from tblEditableContent ec
OUTER APPLY
(
Select map_id from dbo.mapping(ec.map_id) where ec.desc_id =0 or (ec.desc_id =1 and map_id=ec.map_id)
) ca
group by ca.map_id
)ca
on ca.isExcluded=0 and ec.map_id=ca.map_id

SQL Server query to retrieve all from View

I have a SQL View, similiar to the one below:
map_id | type_id | path
1 1 0
2 2 0
3 3 0
4 1 A>B
5 1 A>B>C
6 2 T>Z
7 2 T>Z>X
8 3 U
9 3 X>Y
10 1 D
And another table, tblRoles
role_group_id | type_id | map_id
1 1 1
2 1 4
I want to build a query that will include all map_id where role_group_id has the map_id = 1 and for the rest it should only get the corresponding map_id
So, the query result should look like:
role_group_id | type_id | map_id | path
1 1 4 A>B
1 1 5 A>B>C
1 1 10 D
1 1 1 0
2 1 4 A>B
Could someone point me to the correct way?
Thank you!
Haven't tested this yet, I hope this should work.
SELECT r.role_group_id
,r.type_id
,sv.map_id
,sv.path
FROM tblRoles r
LEFT JOIN sampleView sv ON r.type_id = sv.type_id
WHERE r.map_id = 1
UNION
SELECT r.role_group_id
,r.type_id
,r.map_id
,sv.path
FROM tblRoles r
INNER JOIN sampleView sv ON r.map_id = sv.map_id
WHERE r.map_id <> 1
You can use UNION
#rosuandreimihai: select all when map_id=1 and if map_id is different, select only the
corresponding row
DECLARE #CurrentMapId INT = 1
SELECT * FROM FirstTable
WHERE
TYPE_ID IN
(
SELECT r.type_id FROM tblRoles r
WHERE
map_id = #CurrentMapId
)
UNION
SELECT * FROM FirstTable
WHERE
map_id IN
(
SELECT r.map_id FROM tblRoles r
WHERE
#CurrentMapId = 1 OR
map_id = #CurrentMapId
)

Column into rows and make a master table of columns

I need insert columns data to a different table and create master table for columns
Like example : I need to convert table tblcatData into two tables tblCat and tblcatDataNew
tblCatData
Primaykey | A | B | C | D | D | F | G | H | I | J | K | L | M |
--------------------------------------------------------------------
1 | 1 | 2 | 3 | 5 | 5 | 5 | 3 | 3 | 3 | 1 | 4 | 1 | 1 |
2 | 1 | 2 | 5 | 5 | 5 | 5 | 3 | 5 | 3 | 1 | 1 | 5 | 1 |
3 | 5 | 2 | 3 | 5 | 5 | 5 | 5 | 3 | 3 | 1 | 1 | 1 | 4 |
tblCat
PrimaryKey | Category
----------------------------
1 | A
2 | B
3 | C
4 | D
5 | E
6 | F
7 | G
. .
. .
. .
tblCatDataNew
PrimaryKey | FK_CatID | Data |
-----------------------------------
1 | 1 | 1 |
2 | 1 | 1 |
3 | 1 | 5 |
4 | 2 | 2 |
5 | 2 | 2 |
6 | 2 | 2 |
7 | 3 | 3 |
8 | 3 | 5 |
. . .
. . .
. . .
You could try the following scenario:
Create tblCat.
Create tblCatDataNew with the following deviations from the original design:
the FK_CatID column is allowed to accept NULLs temporarily (or maybe permanently, if that was your original intention);
an extra column is added temporarily to receive the category names from the original table.
Unpivot tblCatData and insert the results into tblCatDataNew (the values into Data and the column names, as category names, into the temporary column).
Select all the distinct category names from tblCatDataNew and insert them into tblCat. (That will produce the key values for them.)
Update the foreign keys in tblCatDataNew from tblCat, joining the two tables by category names.
Drop the temporary column from tblCatDataNew.
Set tblCatDataNew.FK_CatID as NOT NULL (that is, if you wanted it to be so).
Here's the entire test script, including creation of the original table (in case someone would like to try it):
BEGIN TRANSACTION
GO
/* prepare the original table, for tests */
WITH data (
Primaykey, A, B, C, D, E, F, G, H, I, J, K, L, M
) AS (
SELECT 1 , 1, 2, 3, 5, 5, 5, 3, 3, 3, 1, 4, 1, 1 UNION ALL
SELECT 2 , 1, 2, 5, 5, 5, 5, 3, 5, 3, 1, 1, 5, 1 UNION ALL
SELECT 3 , 5, 2, 3, 5, 5, 5, 5, 3, 3, 1, 1, 1, 4
)
SELECT * INTO tblCatData FROM data;
GO
/* Step 1 */
CREATE TABLE tblCat (
PrimaryKey int IDENTITY CONSTRAINT PK_tblCat PRIMARY KEY,
Category varchar(50) NOT NULL
);
GO
/* Step 2 */
CREATE TABLE tblCatDataNew (
PrimaryKey int IDENTITY CONSTRAINT PK_tblCatDataNew PRIMARY KEY,
FK_CatID int NULL CONSTRAINT FK_tblCatDataNew_tblCat FOREIGN KEY REFERENCES tblCat (PrimaryKey),
Data int,
Category varchar(50)
);
GO
/* Step 3 */
INSERT INTO tblCatDataNew (
Data,
Category
)
SELECT
Data,
Category
FROM tblCatData
UNPIVOT (
Data for Category IN (A, B, C, D, E, F, G, H, I, J, K, L, M)
) u
ORDER BY
Category,
Primaykey;
GO
/* Step 4 */
INSERT INTO tblCat (Category)
SELECT DISTINCT Category
FROM tblCatDataNew
GO
/* Step 5 */
UPDATE tblCatDataNew
SET FK_CatID = c.PrimaryKey
FROM tblCat c
WHERE tblCatDataNew.Category = c.Category
GO
/* Step 6 */
ALTER TABLE tblCatDataNew
DROP COLUMN Category
GO
/* Step 7 */
ALTER TABLE tblCatDataNew
ALTER COLUMN FK_CatID int NOT NULL
GO
/* view the results */
SELECT * FROM tblCat
SELECT * FROM tblCatDataNew
GO
ROLLBACK TRANSACTION
Note that the UNPIVOT clause is supported in SQL Server starting from the 2005 version. In earlier versions you'd have to use a different method to unpivot data (Step 3), e.g. like this:
INSERT INTO tblCatDataNew (
Data,
Category
)
SELECT
Data = CASE x.CatNum
WHEN 1 THEN A
WHEN 2 THEN B
WHEN 3 THEN C
WHEN 4 THEN D
WHEN 5 THEN E
WHEN 6 THEN F
WHEN 7 THEN G
WHEN 8 THEN H
WHEN 9 THEN I
WHEN 10 THEN J
WHEN 11 THEN K
WHEN 12 THEN L
WHEN 13 THEN M
END,
Category = CASE x.CatNum
WHEN 1 THEN 'A'
WHEN 2 THEN 'B'
WHEN 3 THEN 'C'
WHEN 4 THEN 'D'
WHEN 5 THEN 'E'
WHEN 6 THEN 'F'
WHEN 7 THEN 'G'
WHEN 8 THEN 'H'
WHEN 9 THEN 'I'
WHEN 10 THEN 'J'
WHEN 11 THEN 'K'
WHEN 12 THEN 'L'
WHEN 13 THEN 'M'
END
FROM tblCatData
CROSS JOIN (
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9 UNION ALL
SELECT 10 UNION ALL
SELECT 11 UNION ALL
SELECT 12 UNION ALL
SELECT 13
) x (CatNum)
ORDER BY
Category,
Primaykey;
or even like this:
INSERT INTO tblCatDataNew (
Data,
Category
)
SELECT
Data = CASE x.CatNum
WHEN 1 THEN A
WHEN 2 THEN B
WHEN 3 THEN C
WHEN 4 THEN D
WHEN 5 THEN E
WHEN 6 THEN F
WHEN 7 THEN G
WHEN 8 THEN H
WHEN 9 THEN I
WHEN 10 THEN J
WHEN 11 THEN K
WHEN 12 THEN L
WHEN 13 THEN M
END,
Category = x.CatName
FROM tblCatData
CROSS JOIN (
SELECT 1, 'A' UNION ALL
SELECT 2, 'B' UNION ALL
SELECT 3, 'C' UNION ALL
SELECT 4, 'D' UNION ALL
SELECT 5, 'E' UNION ALL
SELECT 6, 'F' UNION ALL
SELECT 7, 'G' UNION ALL
SELECT 8, 'H' UNION ALL
SELECT 9, 'I' UNION ALL
SELECT 10, 'J' UNION ALL
SELECT 11, 'K' UNION ALL
SELECT 12, 'L' UNION ALL
SELECT 13, 'M'
) x (CatNum, CatName)
ORDER BY
Category,
Primaykey;
Here are the results that the above script produced for me:
tblCat:
PrimaryKey Category
----------- --------------------------------------------------
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
11 K
12 L
13 M
tblCatDataNew:
PrimaryKey FK_CatID Data
----------- ----------- -----------
1 1 1
2 1 1
3 1 5
4 2 2
5 2 2
6 2 2
7 3 3
8 3 5
9 3 3
10 4 5
11 4 5
12 4 5
13 5 5
14 5 5
15 5 5
16 6 5
17 6 5
18 6 5
19 7 3
20 7 3
21 7 5
22 8 3
23 8 5
24 8 3
25 9 3
26 9 3
27 9 3
28 10 1
29 10 1
30 10 1
31 11 4
32 11 1
33 11 1
34 12 1
35 12 5
36 12 1
37 13 1
38 13 1
39 13 4
You can use union to unpivot the columns, and select ... into to store the result in a new table:
select PrimaryKey
, FK_CatId
, Category
into tblCatDataNew
from (
select PrimaryKey
, 1
, A
from tblCatData
union all
select PrimaryKey
, 2
, B
from tblCatData
union all
...
)

Resources