Cross table query on a single queston - sql

Here's a table
A B C D R
'a' 1 3 1 0
'a' 2 3 1 1
'a' 2 3 1 0
'b' 1 3 1 1
'b' 2 4 3 2
'b' 1 4 3 0
'c' 2 4 3 0
The cross tabulation of R with respect to A is as follows:
A R count
a 0 2
a 1 1
a 2 0
b 0 1
b 1 1
b 2 1
c 0 1
c 1 0
c 2 0
The cross tabulation gives the frequency of R's value depending on A.
What is the sql query for this operation?

I would use CROSS JOIN and LEFT JOIN
select ta.a, tr.r, count(t.a)
from (select distinct a from tab) TA
cross join (select distinct r from tab) TR
left join tab t on t.a = ta.a and t.r = tr.r
group by ta.a, tr.r

Using CROSS JOIN and a conditional sum to achieve the count:
declare #Table table (A char(1), B int, C int, D int, R int)
Insert into #Table values
('a', 1, 3, 1, 0)
, ('a', 2, 3, 1, 1)
, ('a', 2, 3, 1, 0)
, ('b', 1, 3, 1, 1)
, ('b', 2, 4, 3, 2)
, ('b', 1, 4, 3, 0)
, ('c', 2, 4, 3, 0)
select A, x.R, Sum(iif(x.r = t.R, 1, 0))
from #Table t
cross join (Select distinct R from #Table) x
group by A, x.R
order by a, x.R

Related

Match rows that include one of each at least once in SQL

I have a users table:
ID Name OID TypeID
1 a 1 1
2 b 1 2
3 c 1 3
4 d 2 1
5 e 2 1
6 f 2 2
7 g 3 2
8 h 3 2
9 i 3 2
for this table, I want to filter by OID and TypeID so that I get the rows that it is filtered by OID and that includes all 1, 2, and 3 in TypeID.
For example, where OID=1, we have 1, 2, and 3 in TypeID but I shouldn't get the rows with IDs 4-6 because for IDs 4-6, OIDs are the same but TypeID does not include all of each(1, 2, and 3).
You can do :
select oid
from table t
where typeid in (1,2,3)
group by oid
having count(*) = 3;
If, oid contain duplicate typeid then you can use count(distinct typeid) instead.
you could use exists
select oid from table t1
where exists ( select 1 from table t1 where t1.oid=t2.oid
group by t2.oid
having (distinct TypeID)=3
)
Asume TypeID 1,2,3
if you are using sql-server, you can try this.
DECLARE #SampleData TABLE(ID INT, Name VARCHAR(5), OID INT, TypeID INT)
INSERT INTO #SampleData VALUES
(1 , 'a', 1, 1),
(2 , 'b', 1, 2),
(3 , 'c', 1, 3),
(4 , 'd', 2, 1),
(5 , 'e', 2, 1),
(6 , 'f', 2, 2),
(7 , 'g', 3, 2),
(8 , 'h', 3, 2),
(9 , 'i', 3, 2)
SELECT * FROM #SampleData D
WHERE NOT EXISTS (
SELECT * FROM #SampleData D1
RIGHT JOIN (VALUES (1),(2),(3)) T(TypeID) ON D1.TypeID = T.TypeID
AND D.OID = D1.OID
WHERE D1.TypeID IS NULL
)
Result:
ID Name OID TypeID
----------- ----- ----------- -----------
1 a 1 1
2 b 1 2
3 c 1 3

how to do permutations combined with permutations in sql

I have created a table that does all the 3 letter combinations of the alphabet. See below
ID 1stLetter 2ndLetter 3rdLetter
1 A B C
2 A B D
3 A B E
and so on
I have a second table as follows:
Letter Number
A 0
A 1
B 0
B 1
C 0
D 0
D 1
E 0
E 1
E 2
I have been trying to figure out how to combine the two so that for each row of the first table it is combined with the second table to get all possible combinations. For example, the output should look like:
NEWID ID 1stLetter 2ndLetter 3rdLetter 1stNumber 2ndNumber 3rd Number
1 1 A B C 0 0 0
2 1 A B C 1 0 0
3 1 A B C 0 1 0
4 1 A B C 1 1 0
5 2 A B D 0 0 0
6 2 A B D 0 0 1
7 2 A B D 0 1 0
8 2 A B D 0 0 1
9 2 A B D 1 0 0
10 2 A B D 1 0 1
11 2 A B D 1 1 0
12 2 A B D 1 0 1
13 2 A B D 1 1 1
14 3 A B E and so on
Is this possible? Any help will be greatly appreciated.
Thanks,
J
This should do the trick, hopefully the code is self explanatory, please ask if not:
CREATE TABLE #combo
(
id INT ,
letter1 NVARCHAR(1) ,
letter2 NVARCHAR(1) ,
letter3 NVARCHAR(1)
);
CREATE TABLE #numbers
(
letter NVARCHAR(1) ,
number INT
);
INSERT INTO #combo
( id, letter1, letter2, letter3 )
VALUES ( 1, 'A', 'B', 'C' ),
( 2, 'A', 'B', 'D' ),
( 3, 'A', 'B', 'E' );
INSERT INTO #numbers
( letter, number )
VALUES ( 'A', 0 ),
( 'A', 1 ),
( 'B', 0 ),
( 'B', 1 ),
( 'C', 0 ),
( 'C', 1 ),
( 'D', 0 ),
( 'D', 1 ),
( 'E', 0 ),
( 'E', 1 ),
( 'E', 2 );
SELECT c.* ,
n1.number ,
n2.number ,
n3.number
FROM #combo c
INNER JOIN #numbers n1 ON c.letter1 = n1.letter
INNER JOIN #numbers n2 ON c.letter2 = n2.letter
INNER JOIN #numbers n3 ON c.letter3 = n3.letter
ORDER BY id;
DROP TABLE #combo;
DROP TABLE #numbers;
Jus use CROSS JOIN
SELECT * FROM [data source1] CROSS JOIN [data source2]

SQL aggregation on the latest output per machine for each time

I have the following table:
ID machine app output time
1 1 A 12 1
2 1 B 15 1
3 1 B 8 3
4 1 A 11 4
5 2 C 14 4
6 2 D 17 4
For each app I want to get the latest output given up to each point in time, and aggregate these results grouped by machine using AVG
So for the table on top, the data before aggregation should be:
time machine app latest
1 1 A 12
1 1 B 15
3 1 A 12
3 1 B 8
4 1 A 11
4 1 B 8
4 2 C 14
4 2 D 17
And the aggregated result should be:
time machine avg
1 1 =(12+15)/2
3 1 =(12+8)/2
4 1 =(11+8)/2
4 2 =(14+17)/2
What is the correct way to approach this problem?
It is not as simple as I thought to be, but I think it works just as You want. I changed time column to ts, like this:
CREATE TABLE Table1
(ID int, machine int, app char(1), output int, ts int)
;
INSERT INTO Table1
(ID,machine,app,output, ts)
VALUES
(1, 1, 'A', 12, 1),
(2, 1, 'B', 15, 1),
(3, 1, 'B', 8, 3),
(4, 1, 'A', 11, 4),
(5, 2, 'C', 14, 4),
(6, 2, 'D', 17, 4)
;
And here is the query:
WITH
times as
(
SELECT distinct ts FROM Table1
),
machine_apps as
(
SELECT DISTINCT machine,app FROM Table1
),
grid as
(
SELECT
ts,machine,app
FROM
times
CROSS JOIN machine_apps
),
last_outputs as
(
SELECT
g.ts,
g.app,
g.machine,
max(t.ts) as last_time
FROM
grid g
JOIN Table1 t ON (t.app = g.app AND t.machine = g.machine AND t.ts <= g.ts)
GROUP BY
g.ts,
g.app,
g.machine
)
SELECT
l.ts,
l.machine,
AVG(t.output) as avg
FROM
last_outputs l
LEFT JOIN Table1 t ON (t.app = l.app AND t.machine = l.machine AND t.ts = l.last_time)
GROUP BY
l.ts,
l.machine
ORDER BY
l.ts,
l.machine

CTE parent-child showing siblings

I have a CTE-query that displays a tree using recursion. This works great when displaying the whole tree. But I want to pass in ID as a variable and include the siblings for each current node.
Testcode:
DECLARE #TT TABLE
(
ID int,
Name varchar(25),
ParentID int,
SortIndex int
)
INSERT #TT
SELECT 1, 'A', NULL, 1 UNION ALL
SELECT 2, 'B_1', 3, 1 UNION ALL
SELECT 3, 'B', 1, 2 UNION ALL
SELECT 4, 'B_2', 3, 2 UNION ALL
SELECT 5, 'C', 1, 3 UNION ALL
SELECT 6, 'C_2', 5, 2 UNION ALL
SELECT 7, 'A_1', 1, 1 UNION ALL
SELECT 8, 'A_2', 1, 2 UNION ALL
SELECT 9, 'C_1', 5, 1
;WITH CTETree
AS
(
SELECT *, CAST(NULL AS VARCHAR(25)) AS ParentName, 1 AS Lev,
CAST(ROW_NUMBER() OVER(ORDER BY SortIndex) AS VARBINARY(MAX)) AS SortPath
FROM #TT
WHERE ParentID IS NULL
UNION ALL
SELECT F.*, CTETree.Name AS ParentName, Lev + 1,
SortPath + CAST(ROW_NUMBER() OVER(ORDER BY F.SortIndex) AS BINARY(32))
FROM #TT AS F
INNER JOIN CTETree
ON F.ParentID = CTETree.ID
)
SELECT * FROM CTETree
ORDER BY SortPath
/*
DESIRED RESULT:
WHEN ID = 3 PASSED IN:
1 A NULL 1 NULL 1
3 B 1 2 A 2
2 B_1 3 1 B 3
4 B_2 3 2 B 3
5 C 1 3 A 2
WHEN ID = 1 PASSED IN:
1 A NULL 1 NULL 1
3 B 1 2 A 2
5 C 1 3 A 2
WHEN ID = 9 PASSED IN:
1 A NULL 1 NULL 1
3 B 1 2 A 2
5 C 1 3 A 2
9 C_1 5 1 C 3
6 C_2 5 2 C 3
*/
SQL-Fiddle: http://sqlfiddle.com/#!3/d41d8/5526
Only in once I confront a question where disappear A_1 and A_2. Therefore add in exception(in case clause).
If you need this records delete this expression AND f.ID != 7 AND f.ID != 8.
DECLARE #ID int = 3 -- variable wich you want pass
DECLARE #Matched int = (SELECT CASE WHEN ParentID = 1 THEN ID ELSE ParentID END FROM #TT WHERE ID = #ID)
;WITH CTETree
AS
(
SELECT *, CAST(NULL AS VARCHAR(25)) AS ParentName, 1 AS Lev,
CAST(ROW_NUMBER() OVER(ORDER BY SortIndex) AS VARBINARY(MAX)) AS SortPath,
0 AS Matched
FROM #TT
WHERE ParentID IS NULL
UNION ALL
SELECT F.*, cte.Name AS ParentName, cte.Lev + 1,
SortPath + CAST(ROW_NUMBER() OVER(ORDER BY F.SortIndex) AS BINARY(32)),
CASE WHEN (cte.ID = 1 AND f.ID != 7 AND f.ID != 8)
OR (cte.ID = #Matched AND cte.Lev + 1 > 2)
THEN 1 END AS Matched
FROM #TT AS F INNER JOIN CTETree cte
ON F.ParentID = cte.ID
)
SELECT ID, Name, ParentID, SortIndex, ParentName, Lev FROM CTETree
WHERE Matched IS NOT NULL
ORDER BY SortPath
Demo on SQLFiddle

Select continuous ranges from table

I need to extract continous ranges from a table based on consecutive numbers (column N) and same "category" these numbers relate to (column C below). Graphically it looks like this:
N C D
--------
1 x a C N1 N2 D1 D2
2 x b ------------------
3 x c x 1 4 a d (continuous range with same N)
4 x d ==> x 6 7 e f (new range because "5" is missing)
6 x e y 8 10 g h (new range because C changed to "y")
7 x f
8 y g
9 y h
10 y i
SQL Server is 2005. Thanks.
DECLARE #myTable Table
(
N INT,
C CHAR(1),
D CHAR(1)
)
INSERT INTO #myTable(N,C,D) VALUES(1, 'x', 'a');
INSERT INTO #myTable(N,C,D) VALUES(2, 'x', 'b');
INSERT INTO #myTable(N,C,D) VALUES(3, 'x', 'c');
INSERT INTO #myTable(N,C,D) VALUES(4, 'x', 'd');
INSERT INTO #myTable(N,C,D) VALUES(6, 'x', 'e');
INSERT INTO #myTable(N,C,D) VALUES(7, 'x', 'f');
INSERT INTO #myTable(N,C,D) VALUES(8, 'y', 'g');
INSERT INTO #myTable(N,C,D) VALUES(9, 'y', 'h');
INSERT INTO #myTable(N,C,D) VALUES(10, 'y', 'i');
WITH StartingPoints AS(
SELECT A.*, ROW_NUMBER() OVER(ORDER BY A.N) AS rownum
FROM #myTable AS A
WHERE NOT EXISTS(
SELECT *
FROM #myTable B
WHERE B.C = A.C
AND B.N = A.N - 1
)
),
EndingPoints AS(
SELECT A.*, ROW_NUMBER() OVER(ORDER BY A.N) AS rownum
FROM #myTable AS A
WHERE NOT EXISTS (
SELECT *
FROM #myTable B
WHERE B.C = A.C
AND B.N = A.N + 1
)
)
SELECT StartingPoints.C,
StartingPoints.N AS [N1],
EndingPoints.N AS [N2],
StartingPoints.D AS [D1],
EndingPoints.D AS [D2]
FROM StartingPoints
JOIN EndingPoints ON StartingPoints.rownum = EndingPoints.rownum
Results:
C N1 N2 D1 D2
---- ----------- ----------- ---- ----
x 1 4 a d
x 6 7 e f
y 8 10 g i
The RANK function is a safer bet than ROW_NUMBER, in case any N values are duplicated, as in the following example:
declare #ncd table(N int, C char, D char);
insert into #ncd
select 1,'x','a' union all
select 2,'x','b' union all
select 3,'x','c' union all
select 4,'x','d' union all
select 4,'x','e' union all
select 7,'x','f' union all
select 8,'y','g' union all
select 9,'y','h' union all
select 10,'y','i' union all
select 10,'y','j';
with a as (
select *
, r = N-rank()over(partition by C order by N)
from #ncd
)
select C=MIN(C)
, N1=MIN(N)
, N2=MAX(N)
, D1=MIN(D)
, D2=MAX(D)
from a
group by r;
Result, which correctly withstands the duplicated 4 and 10:
C N1 N2 D1 D2
---- ----------- ----------- ---- ----
x 1 4 a e
x 7 7 f f
y 8 10 g j
Using this answer as a starting point, I ended up with the following:
;
WITH data (N, C, D) AS (
SELECT 1, 'x', 'a' UNION ALL
SELECT 2, 'x', 'b' UNION ALL
SELECT 3, 'x', 'c' UNION ALL
SELECT 4, 'x', 'd' UNION ALL
SELECT 6, 'x', 'e' UNION ALL
SELECT 7, 'x', 'f' UNION ALL
SELECT 8, 'y', 'g' UNION ALL
SELECT 9, 'y', 'h' UNION ALL
SELECT 10, 'y', 'i'
),
ranked AS (
SELECT
curr.*,
Grp = curr.N - ROW_NUMBER() OVER (PARTITION BY curr.C ORDER BY curr.N),
IsStart = CASE WHEN pred.C IS NULL THEN 1 ELSE 0 END,
IsEnd = CASE WHEN succ.C IS NULL THEN 1 ELSE 0 END
FROM data AS curr
LEFT JOIN data AS pred ON curr.C = pred.C AND curr.N = pred.N + 1
LEFT JOIN data AS succ ON curr.C = succ.C AND curr.N = succ.N - 1
)
SELECT
C,
N1 = MIN(N),
N2 = MAX(N),
D1 = MAX(CASE IsStart WHEN 1 THEN D END),
D2 = MAX(CASE IsEnd WHEN 1 THEN D END)
FROM ranked
WHERE 1 IN (IsStart, IsEnd)
GROUP BY C, Grp
Write a stored procedure. It will create and fill a temporary table witch will contain C, N1, N2, D1 and D2 columns.
Create the temporary table
use a cursor to loop on entries in table containing N, C, D ordered by N
use a variable to detect a new range (Ni < N(i-1)-1) and to store N1, N2, D1 and D2
INSERT into the temporary table for each range detected (new range detected or and of the cursor)
Tell me if you need a code example.