What would be my SqlServer query? - sql-server

Table1
P R E Value
X 1 10 1
Y 2 30 2
Z 3 CR 3
X 1 30 4
Table2
P R E Value
X 1 CR 4
Y 2 10 5
Y 3 CR 6
W 1 30 7
Query1 - Merge these two tables. I'm able to achieve this using union clause.
Query2 - On the merged table select all records except for entries where for a combination of P, R & E; there are similar records with the only mismatch of 'E' as 30 & 10, then ignore record with E as 30. In case only 30 is present then consider it.
Conditions:
10 & 30 - consider only 10, ignore 30
10 - consider it
30 - consider it
CR - consider it
10 & CR - consider both
30 & CR - consider both
10 & 30 & CR - consider 10 & CR
Expected Output table
P R E Value
X 1 10 1
Z 3 CR 3
X 1 CR 4
Y 2 10 5
Y 3 CR 6
W 1 30 7
Ignored records
Y 2 30 2
X 1 30 4

I was able to achieve your desired result set with the following query.
insert into #Table1
values ('X','1','10','1'),
('Y','2','30','2'),
('Z','3','CR','3'),
('X','1','30','4')
insert into #Table2
values ('X','1','CR','4'),
('Y','2','10','5'),
('Y','3','CR','6'),
('W','1','30','7')
--Query2
select * from(
--Query1
select * from #Table1 union select * from #Table2) x
where E != '30' OR
(
E = '30' AND P+':'+R NOT IN
(
--Modified Query1
select P+':'+R from #Table1 where E = '10'
union
select P+':'+R from #Table2 y where E = '10'
)
)
order by Value

Related

TSQL Selection Criteria For Join - Left Join

Select *
From cacheAttendanceMeasures cam
Left Join dmcUserSelectedAttendanceMeasures usam on usam.attMeasureID = cam.attMeasureID
And usam.personID = #personID
And usam.pageID = #pageID
I understand that the above query will return all rows from cacheAttendanceMeasures as well as any rows from dmcUserSelectedAttendanceMeasures and dmcUserSelectedStudentMonths where the conditions match. What I would really like it to do is this:
If the left join(s) don't match, then return all rows from cacheAttendanceMeasures -- so what it is currently doing
However, if the left join(s) DO match, then return ONLY the rows that match from cacheAttendanceMeasures
Is this possible?
EDIT:
I simplified the query above to only include one Left Join table, I don't want to over-complicate the issue.
Below are dataset examples I'd like to see returned based on if there are no matches between the tables versus there is a match:
cam Table
cID attMeasureID Value
1 1 530
2 2 95.7
3 3 380
4 4 742.57
5 5 200
usam Table
uID personID pageID attMeasureID
1 877450 31 1
2 923450 28 2
3 877450 31 3
4 369842 28 4
5 212193 25 1
Dataset to Return if #personID = 577597 & #pageID = 20:
CID attMeasureID Value uID pageID attMeasureID
1 1 530 null nul null
2 2 95.7 null null null
3 3 380 null null null
4 4 742.57 null null null
5 5 200 null null null
Dataset to Return if #personID = 877450 & #pageID = 31:
CID attMeasureID Value uID personID pageID attMeasureID
1 1 530 1 877450 31 1
3 3 380 3 877450 31 3
I'm not really sure what results you want...
At first I thought maybe something like this..
SELECT *
FROM cacheAttendanceMeasures cam
LEFT JOIN dmcUserSelectedAttendanceMeasures usam
on usam.attMeasureID = cam.attMeasureID
And usam.personID = #personID
And usam.pageID = #pageID
LEFT JOIN dmcUserSelectedStudentMonths ussm
on ussm.monthSeq = cam.pupilMonth
And ussm.personID = USAM.PersonID
And ussm.pageID = USAM.PageId
--This where clause would seem silly as it negates the left joins; making them inner joins and violates #1 as to what you're after. so I don't think that's what you're after...
WHERE ussm.personID is not null
and usam.personID is not null
So with rule 2 are you saying... if a single record isn't null, then only return records which exist in both Left joined tables and the 1st table?
So given:
T1 T2 T3
X X X
Y NULL Y
Z Y NULL
T NULL NULL
I think you would want just record X
But Given
So given:
T1 T2 T3
X X NULL
Y NULL Y
Z Y NULL
T NULL NULL
I think you would want X, Y Z, and T... need better clairification.

SQL Server view for calc ParentId column

I have a table with following sampled rows:
Id CompanyId value
1 X a
2 X b
3 X c
4 X d
5 Y e
6 Y f
7 z g
8 Z h
9 X i
10 X j
I want to have a view with following result :
Id CompanyId ParentId
1 X NULL
2 X NULL
3 X NULL
4 X NULL
5 Y 1
6 Y 1
7 z 5
8 Z 5
9 X 7
10 X 7
On this result calculated ParentId for each row. ParentId for each row equal to Id of first row from previous companyId.
I hope that SQL Server 2012 tag is here for reason :)
;with x as (
select *, lag(companyid) over(order by id) as prev
from #t1
),
y as (
select *, sum(case when prev = companyid then 0 else 1 end) over(order by id) as grp
from x
)
select y.id, y.companyid, min(y1.id)
from y
left join y y1 on y1.grp = y.grp-1
group by y.id, y.companyid

SQL Server NTILE - Same value in different quartile

I have a scenario where i'm splitting a number of results into quartilies using the SQL Server NTILE function below. The goal is to have an as equal number of rows in each class
case NTILE(4) over (order by t2.TotalStd)
when 1 then 'A' when 2 then 'B' when 3 then 'C' else 'D' end as Class
The result table is shown below and there is a (9,9,8,8) split between the 4 class groups A,B,C and D.
There are two results which cause me an issue, both rows have a same total std value of 30 but are assigned to different quartiles.
8 30 A
2 30 B
I'm wondering is there a way to ensure that rows with the same value are assigned to the same quartile? Can i group or partition by another column to get this behaviour?
Pos TotalStd class
1 16 A
2 23 A
3 21 A
4 29 A
5 25 A
6 26 A
7 28 A
8 30 A
9 29 A
1 31 B
2 30 B
3 32 B
4 32 B
5 34 B
6 32 B
7 34 B
8 32 B
9 33 B
1 36 C
2 35 C
3 35 C
4 35 C
5 40 C
6 38 C
7 41 C
8 43 C
1 43 D
2 48 D
3 45 D
4 47 D
5 44 D
6 48 D
7 46 D
8 57 D
You will need to re create the Ntile function, using the rank function.
The rank function gives the same rank for rows with the same value. The value later 'jumps' to the next rank as if you used row_number.
We can use this behavior to mimic the Ntile function, forcing it to give the same Ntile value to rows with the same value. However - this will cause the Ntile partitions to be with a different size.
See the example below for the new Ntile using 4 bins:
declare #data table ( x int )
insert #data values
(1),(2),
(2),(3),
(3),(4),
(4),(5)
select
x,
1+(rank() over (order by x)-1) * 4 / count(1) over (partition by (select 1)) as new_ntile
from #data
Results:
x new_ntile
---------------
1 1
2 1
2 1
3 2
3 2
4 3
4 3
5 4
Not sure what you're expecting to happen here, really. SQL Server has divided up the data into 4 groups of as-equal-size-as-possible, as you asked. What do you want to happen? Have a look at this example:
declare #data table ( x int )
insert #data values
(1),(2),
(2),(3),
(3),(4),
(4),(5)
select
x,
NTILE(4) over (order by x) as ntile
from #data
Results:
x ntile
----------- ----------
1 1
2 1
2 2
3 2
3 3
4 3
4 4
5 4
Now every ntile group shares a value with the one(s) next to it! But what else should it do?
Try this:
; with a as (
       select TotalStd,Class=case ntile(4)over( order by TotalStd )
                                when 1 then 'A'
                                when 2 then 'B'
                                when 3 then 'C'
                                when 4 then 'D'
                                end
                from t2
                group by TotalStd
)
select d.*, a.Class from t2 d
inner join a on a.TotalStd=d.TotalStd
order by Class,Pos;
Here we have a table of 34 rows.
DECLARE #x TABLE (TotalStd INT)
INSERT #x (TotalStd) VALUES (16), (21), (23), (25), (26), (28), (29), (29), (30), (30), (31), (32), (32), (32), (32), (33), (34),
(34), (35), (35), (35), (36), (38), (40), (41), (43), (43), (44), (45), (46), (47), (48), (48), (57)
SELECT '#x', TotalStd FROM #x ORDER BY TotalStd
We want to divide into quartiles. If we use NTILE, the bucket sizes will be roughly the same size (8 to 9 rows each) but ties are broken arbitrarily:
SELECT '#x with NTILE', TotalStd, NTILE(4) OVER (ORDER BY TotalStd) quantile FROM #x
See how 30 appears twice: once in quantile 1 and once in quantile 2. Similarly, 43 appears both in quantiles 3 and 4.
What I ought to find is 10 items in quantile 1, 8 in quantile 2, 7 in quantile 3 and 9 in quantile 4 (i.e. not a perfect 9-8-9-8 split, but such a split is impossible if we are not allowed to break ties arbitrarily). I can do it using NTILE to determine cutoff points in a temporary table:
DECLARE #cutoffs TABLE (quantile INT, min_value INT, max_value INT)
INSERT #cutoffs (quantile, min_value)
SELECT y.quantile, MIN(y.TotalStd)
FROM (SELECT TotalStd, NTILE(4) OVER (ORDER BY TotalStd) AS quantile FROM #x) y
GROUP BY y.quantile
-- The max values are the minimum values of the next quintiles
UPDATE c1 SET c1.max_value = ISNULL(C2.min_value, (SELECT MAX(TotalStd) + 1 FROM #x))
FROM #cutoffs c1 LEFT OUTER JOIN #cutoffs c2 ON c2.quantile - 1 = c1.quantile
SELECT '#cutoffs', * FROM #cutoffs
We'll use the the boundary values in the #cutoffs table to create the final table:
SELECT x.TotalStd, c.quantile FROM #x x
INNER JOIN #cutoffs c ON x.TotalStd >= c.min_value AND x.TotalStd < c.max_value

how to acomplish a full series in sql

I want to achieve a full numeric scale from 0 to the max number in the table.
Let's say we have a table T with two fields named x and y
select x,y
from t
would show us lets say the results
X Y
3 11
5 23
7 45
9 1
10 34
I found this query to build sequential numbers:
With T_Misparim As
(Select 1 N
Union All
Select N+1 N
From T_Misparim
Where N<1000)
Select N
From T_Misparim
Option (MaxRecursion 0);
from this source : http://www.sqlserver.co.il/?p=3296
My bottom line is, how do i integrate the two queries into a single query to give
right outer join :
N X Y
0 null 0
1 null 0
2 null 0
3 3 11
4 null 0
5 5 23
6 null 0
7 7 45
8 null 0
9 9 1
10 10 34
You can just LEFT JOIN with the ordinal number CTE;
select 3 as X, 11 as Y into #TEST
insert #TEST values (5,23),(7,45),(9,1),(10,34)
;with NUMS(n) as (
select 0 union all
select 1 + n from NUMS where n < 50
)
select
NUMS.n N,
T.X,
isnull(T.Y, 0) Y
from NUMS
left join #TEST T on (T.X = NUMS.n)
option (maxrecursion 50)
For
N X Y
0 NULL 0
1 NULL 0
2 NULL 0
3 3 11
4 NULL 0
5 5 23
6 NULL 0
7 7 45
8 NULL 0
9 9 1
10 10 34

SQL cross-joining to produce number sequence

I've tried to figure out how this SQL query generates a sequence of numbers, and I still don't have a clue.
Digits Table
digit
--------
0
1
2
3
4
5
6
7
8
9
SELECT D3.digit * 100 + D2.digit * 10 + D1.digit + 1 AS n
FROM dbo.Digits as D1
CROSS JOIN dbo.Digits as D2
CROSS JOIN dbo.Digits AS D3
ORDERY BY n;
The Query Result...
n
------
1
2
3
4
5
...
998
999
1000
How does it work?
If you are into CTE, this will give you 1 to 1000.
;
with
Num(Pos) as
(
select cast(1 as int)
union all
select cast(Pos + 1 as int) from Num where Pos &lt 1000
)
select * from Num option (maxrecursion 0)
A cross join is a Cartesian product: that is, every row joins with every other row.
So a 11 row table joined to a 7 row table gives 77 rows
In your case, you have 10 rows * 10 rows * 10 rows = 1000.
Try this query to see the raw date before you generate the number
SELECT D3.digit, D2.digit, D1.digit
FROM dbo.Digits as D1
CROSS JOIN dbo.Digits as D2
CROSS JOIN dbo.Digits AS D3
ORDER BY d3, d2, d1;
The way you have 100*d3 + 10*d2 + d1 replicates how we count naturally and carry in addition.
CROSS JOIN is much like an INNER JOIN MYTable on 1 = 1, resulting in the Cartesian Product of your Input Sets
Basically, for each record on the left, it joins for each record on the right.
In the case of a 10-digit source table, the first cross join results in 100 records.
In the case of a second cross join to the same 10-digit source table, you get all 100 previous records again, for each record in the source table, resulting in 1000 records.
Your resulting table would look like this, if you your Select Statement was "Select * ..." Order by ...
D1 D2 D3
1 2 3
1 2 4
1 2 5
If you take those values in the table above and concatenate them (then add one) you get consecutive numbers.
"1" + "2" + "3" = 123 (+1 = 124)
"1" + "2" + "4" = 124 (+1 = 125)
"1" + "2" + "5" = 125 (+1 = 126)
Obviously, the author is not concatenating. However, he's doing the mathematical equivalent.
1 * 100 + 2 * 10 + 3 * 1 + 1 = 124
1 * 100 + 2 * 10 + 4 * 1 + 1 = 125
1 * 100 + 2 * 10 + 5 * 1 + 1 = 126
Ultimately, the author devised a strange way to provide a listing of numbers from 1 to 1000.
The values of the digit from the D3 table will range from 0 - 900 (D3.digit * 100)
The values of the digit from the D2 table will range from 0 - 90 (D2.digit * 10)
The values of the digit from the D1 table will range from 0 - 9 (D1.digit * 100)
Add them up and you have a range from 0 - 999
Add 1 to the result and you have a range from 1 - 1000

Resources