I have a table here :
In the "week No"column ,
I have data for 5 consecutive weeks,every week I want to check these conditions-
1.Every week I will check the current week's points.If the points value is -10 for the current week and the previous week's points is also -10 then -40 is added in table 2(below table) in totalpoints field.
2.Again we check it for current week and prevoius two week's points.If the score is -10 for three weeks consecutively the reward -100 for that person and added to total points field in table 2.(below table)
3.
Similarly for consecutive four weeks i.e current week and the previous 3 week's if points is -10 ,then add -200 to table2's total points field.
Can anyone help me on how to achieve this . M using sql server .
Here it is
; WITH
u AS (SELECT * from (values (1,'Bob'),(2,'Deepak'),(3,'Brinda'),(4,'Chriss'),(4,'Chriss')) as d (usr,name)),
N AS (SELECT * from (values (1,1,-10),(1,2,-10),(1,3,-10),(1,4,-10),(1,5,-10),(2,1,50),(2,2,40),(2,3,30),(2,4,-10),(2,5,-10),(3,1,60),(3,2,20),(3,3,40),(3,4,-10),(3,5,20),(4,1,-10),(4,2,90),(4,3,50),(4,4,30),(4,5,50)) as d (usr,wk,pt)),
sc as (
select w.*, case w.pt when -10 then (case w1.pt when -10 then (case w2.pt when -10 then (case w3.pt when -10 then -200 else -100 end) else -40 end) else 0 end) else 0 end x
from n w
left join n w1 on w.usr = w1.usr and w.wk = w1.wk+1
left join n w2 on w.usr = w2.usr and w.wk = w2.wk+2
left join n w3 on w.usr = w3.usr and w.wk = w3.wk+3
),
l as (
select *, pt+x as total
from sc
),
s as (
select usr, sum(total) total
from l
group by usr
)
select u.*,t.*
from s t
inner join u on u.usr = t.usr
If you want to see all details change final select in
select u.*,t.*
from l t
inner join u on u.usr = t.usr
in your database should be something like:
; WITH
n as (
select [EmployeeName], CAST(SUBSTRING([Week No.],5,10) AS INT) as wk, Points as pt
from YourTable --> Change this to your table
),
sc as (
select w.*, case w.pt when -10 then (case w1.pt when -10 then (case w2.pt when -10 then (case w3.pt when -10 then -200 else -100 end) else -40 end) else 0 end) else 0 end x
from n w
left join n w1 on w.[EmployeeName] = w1.[EmployeeName] and w.wk = w1.wk+1
left join n w2 on w.[EmployeeName] = w2.[EmployeeName] and w.wk = w2.wk+2
left join n w3 on w.[EmployeeName] = w3.[EmployeeName] and w.wk = w3.wk+3
),
l as (
select *, pt+x as total
from sc
),
s as (
select [EmployeeName], sum(total) total
from l
group by [EmployeeName]
)
select *
from s
Related
I am trying to write an SQL query that displays the course popularity,
in descending order.
Course popularity is measured in points, which determined as follows: For every survey:
a. if the votes difference > 10% of total votes, the more popular course gets 1 point, and the less popular course gets 0 points
b. if the votes difference <= 10% of total votes, each course gets 0.5
point
course_id
course_name
faculty
1001
economics_101
business
1002
algebra_101
math
1003
geometry_101
math
1004
management_101
business
1005
marketing_101
business
1006
physics_101
science
survey_id
option_a
option_b
votes_a
votes_b
2001
economics_101
geometry_101
61
34
2002
algebra_101
economics_101
31
68
2003
marketing_101
management_101
11
72
2005
management_101
algebra_101
43
54
2004
geometry_101
marketing_101
48
46
Result achieved so far:
course
popularity
economics_101
4
management_101
2
algebra_101
2
marketing_101
1
geometry_101
1
[NULL]
0
I managed to join it so far, would be great to have inputs on optimizing this query:
WITH x AS
(
WITH b AS
(
WITH a as
(
select * from course c
LEFT JOIN survey s
on c.course_name = s.option_a
UNION ALL
select * from course c
LEFT JOIN survey s
on c.course_name = s.option_b
)
SELECT a.*,
SUM(votes_a+votes_b) as total_votes,
CASE WHEN (a.votes_a - a.votes_b) > (0.1*SUM(votes_a+votes_b)) THEN 1
WHEN (a.votes_b - a.votes_a) <= (0.1*SUM(votes_a+votes_b)) THEN 0.5
ELSE 0
END AS 'Popularity_a',
CASE WHEN (a.votes_b - a.votes_a) > (0.1*SUM(votes_a+votes_b)) THEN 1
WHEN (a.votes_a - a.votes_b) <= (0.1*SUM(votes_a+votes_b)) THEN 0.5
ELSE 0
END AS 'Popularity_b'
FROM
a
GROUP BY
a.course_name ,
a.course_id,
a.faculty ,
a.survey_id ,
a.option_a ,
a.option_b ,
a.votes_a ,
a.votes_b
)
SELECT b.option_a as course,
b.Popularity_a as pop
FROM b
LEFT JOIN
course cx
ON b.option_a = cx.course_name
UNION ALL
SELECT b.option_b as course ,
b.Popularity_b as pop
FROM b
LEFT JOIN
course cx
ON b.option_b = cx.course_name
)
select
x.course ,
sum (x.pop) as popularity
from x
GROUP BY
x.course
order by popularity desc
Use UNION ALL to extract all courses and the respective points they get from the table survey and aggregate to get the popularity.
Then join to course:
WITH
cte AS (
SELECT option_a course_name,
CASE
WHEN votes_a - votes_b > 0.1 * (votes_a + votes_b) THEN 1.0
WHEN votes_b - votes_a > 0.1 * (votes_a + votes_b) THEN 0.0
ELSE 0.5
END points
FROM survey
UNION ALL
SELECT option_b,
CASE
WHEN votes_b - votes_a > 0.1 * (votes_a + votes_b) THEN 1.0
WHEN votes_a - votes_b > 0.1 * (votes_a + votes_b) THEN 0.0
ELSE 0.5
END
FROM survey
),
points AS (
SELECT course_name, SUM(points) total_points
FROM cte
GROUP BY course_name
)
SELECT c.*, COALESCE(p.total_points, 0) popularity
FROM course c LEFT JOIN points p
ON p.course_name = c.course_name
ORDER BY popularity DESC;
See the demo.
I have two queries that look like this:
SELECT
sem.Sem_Jahr,
sem.Sem_KW,
COUNT(*) AS Seminars,
bearb.MA_ID
FROM acc_seminar.t_Seminar sem
JOIN acc_seminar.t_Seminar_Thema semth ON sem.Sem_SemTh_ID = semth.SemTh_ID
JOIN acc_ma.t_Mitarbeiter bearb ON sem.Sem_Berb_MA_ID = bearb.MA_ID
WHERE sem.Sem_Sto != 1 AND semth.SemTh_Typ = 7 AND sem.Sem_Jahr = #Jahr and MA_ID = 372
GROUP BY bearb.MA_ID, sem.Sem_KW, sem.Sem_Jahr
the second query is exactly the same, except the condition is WHERE sem.Sem_Sto != 1 AND semth.SemTh_Typ = 7 AND sem.Sem_Jahr = #Jahr and MA_ID = 372
KW refers to week
I want to show results from both queries, combined. The problem is that the first query may have seminar count value for KW 2, but the second one would have NULL. The problem is, I can't figure out how to join them to get the following desired result:
KW | Seminars from query 1 | Seminars from query 2
----------------------------------------------------
2 | NULL | 5
3 | 8 | NULL
4 | 1 | 4
What I tried:
I tried just putting UNION between these two, but then I only get results from first query.
I also tried to write first query normally and then doing a FULL OUTER JOIN with second query as subquery in JOIN, but then I get results for the first query and results from second query only where the week matches with row from first query.
This whole request seems so banal to me, but I just can't figure it out, it doesn't click in my head on how to join them. Any suggestions?
Alway aim for a minimal, reproducable example. My sample data has way less joins, but should still show your issue and possible solutions.
Sample data
create table data
(
year int,
week int,
flag bit
);
insert into data (year, week, flag) values
(2021, 1, 0),
(2021, 1, 1),
(2021, 1, 1),
(2021, 2, 0),
(2021, 2, 0),
(2021, 2, 0),
(2021, 2, 0),
(2021, 3, 1);
Issue reproduction
Second query as subquery:
select coalesce(f.year, t.year) as year,
coalesce(f.week, t.week) as week,
count(1) as countFalse,
t.countTrue
from data f
full join ( select d.year,
d.week,
count(1) as countTrue
from data d
where d.flag = 1
group by d.year,
d.week ) t
on t.year = f.year
and t.week = f.week
where f.flag = 0 --> issue: week 3 not available for flag = 0, results limited...
group by f.year,
t.year,
f.week,
t.week,
t.countTrue
order by f.year,
f.week;
Result missing week = 3:
year week countFalse countTrue
---- ---- ---------- ---------
2021 1 1 2
2021 2 4 null
Solution 1
Isolate both queries in common table expressions (cte_false, cte_true) and join them without where clause in final select.
with cte_false as
(
select d.year,
d.week,
count(1) as countFalse
from data d
where d.flag = 0
group by d.year,
d.week
),
cte_true as
(
select d.year,
d.week,
count(1) as countTrue
from data d
where d.flag = 1
group by d.year,
d.week
)
select coalesce(f.year, t.year) as year,
coalesce(f.week, t.week) as week,
f.countFalse,
t.countTrue
from cte_false f
full join cte_true t
on t.year = f.year
and t.week = f.week;
Solution 2
Perform all calculations first (cte_count), then use pivot to transform the data.
with cte_count as
(
select d.year,
d.week,
d.flag,
count(1) as countFlag
from data d
group by d.year,
d.week,
d.flag
)
select piv.year,
piv.week,
piv.[0] as countFalse,
piv.[1] as countTrue
from cte_count cc
pivot (max(cc.countFlag) for cc.flag in ([0], [1])) piv;
Result
year week countFalse countTrue
---- ---- ---------- ---------
2021 1 1 2
2021 2 4 null
2021 3 null 1
Fiddle to see things in action.
You can do this using conditional aggregation:
SELECT sem.Sem_Jahr,
sem.Sem_KW,
SUM(CASE WHEN sem.Sem_Sto <> 1 AND semth.SemTh_Typ = 7 AND sem.Sem_Jahr = #Jahr and MA_ID = 372 THEN 1 ELSE 0 END) AS Seminars,
SUM( <whatever the second condition is> THEN 1 ELSE 0 END),
bearb.MA_ID
FROM acc_seminar.t_Seminar sem JOIN
acc_seminar.t_Seminar_Thema semth
ON sem.Sem_SemTh_ID = semth.SemTh_ID JOIN
acc_ma.t_Mitarbeiter bearb
ON sem.Sem_Berb_MA_ID = bearb.MA_ID
GROUP BY bearb.MA_ID, sem.Sem_KW, sem.Sem_Jahr;
If I speculate that the difference is one of the columns, such as semth.SemTh_Typ = 8, then this can be simplified by moving common conditions to the WHERE clause:
SELECT sem.Sem_Jahr,
sem.Sem_KW,
SUM(CASE WHEN semth.SemTh_Typ = 7 THEN 1 ELSE 0 END) AS Seminars,
SUM(CASE WHEN semth.SemTh_Typ = 8 THEN 1 ELSE 0 END),
bearb.MA_ID
FROM acc_seminar.t_Seminar sem JOIN
acc_seminar.t_Seminar_Thema semth
ON sem.Sem_SemTh_ID = semth.SemTh_ID JOIN
acc_ma.t_Mitarbeiter bearb
ON sem.Sem_Berb_MA_ID = bearb.MA_ID
WHERE sem.Sem_Sto <> 1 AND
semth.SemTh_Typ IN (7, 8) AND
sem.Sem_Jahr = #Jahr AND
MA_ID = 372
GROUP BY bearb.MA_ID, sem.Sem_KW, sem.Sem_Jahr;
I have a strange requirement.
Say I have two tables:
ORDER_TB
SEQ PRODUCT_ID ORDER_QTY
1 1 1
2 1 1
3 1 1
STOCK_TB
LOCATION STOCK_QTY
A1 2
B1 1
Desired Join Result:
PRODUCT_ID ORDER_QTY ASSIGNED_LOCATION
1 1 A1
1 1 A1
1 1 B1
In other words, I'd like to assign each products in order table a location from stock_tb based on the quantity of stocks.
This doesn't look like a set operation to me. Is this possible with joins or are there any other clean alternatives in approaching this problem?
What you need to do is get cumulative sums for each of the columns -- this gives you a first and last order for each value. Then you can do a join on a range.
with o as (
select o.*, cumesum
from ORDER_TB o OUTER APPLY
(select sum(o2.order_qty) as cumesum
from ORDER_TB o2
where o2.seq <= o.seq
) o2
),
s as (
select s.*, s2.cumeqty
from STOCK_TB s outer apply
(select sum(s2.order_qty) as cumeqty
from STOCK_TB
where s2.location <= s.location
) s2
)
select o.*, s.location
from o join
s
on o.cumesum between s.cumeqty - s.order_qty + 1 and s.cumeqty;
Note: this works for the data you provided. However, if the quantities in the two tables don't align, then the logic would be more complicated.
Another approach is to use analytic functions:
with cum as
(select s.*, sum(stock_qty) over(order by location) as cum_qty
from stock_tb s)
select x.product_id, x.order_qty, y.location as assigned_location
from (select o.*,
row_number() over(partition by product_id order by seq) as curr_qty
from order_tb o) x
cross join cum y
where y.cum_qty =
(select min(z.cum_qty) from cum z where z.cum_qty >= x.curr_qty)
or (not exists (select 1 from cum z where z.cum_qty > x.curr_qty) and
y.cum_qty = x.curr_qty)
order by seq
Fiddle: http://sqlfiddle.com/#!6/e41b0/1/0
My result of is like this
date acc cr date acc dr
---------------------------------------------------
null null 0 13/3/12 A 1300
null null 0 13/3/12 c 1200
null null 0 13/3/12 D 1100
13/3/12 A 1000 null null 0
18/3/12 E 2000 null null 0
19/3/12 F 3000 null null 0
31/3/12 G 3000 null null 0
this result i got from following query bu joining 2 tables to get cash book
select(case when mli.voucher_type = 1 THEN tav.voucher_date end)pdate,
(case when mli.voucher_type = 1 THEN mli.description end) acc,
(case when tav.voucher_type_id = 1 then sum(tvl.amount) else 0 end) cr,
(case when mli.voucher_type = 2 THEN tav.voucher_dateend) rdate,
(case when mli.voucher_type = 2 THEN mli.description end) acc,
(case when tav.voucher_type_id = 2 then sum(tvl.amount) else 0 end) dr
from t_acc_voucher tav
join t_voucher_ledger tvl on tvl.voucher_id = tav.voucher_id
join m_ledger_index mli on mli.ledger_index_id = tvl.ledger_index_id
group by mli.description, mli.voucher_type, tav.voucher_type_id,tav.voucher_date
I want result like this
date acc cr date acc dr
---------------------------------------------------
13/3/12 A 1000 13/3/12 A 1300
18/3/12 E 2000 13/3/12 c 1200
19/3/12 F 3000 13/3/12 D 1100
31/3/12 G 3000 null null 0
can any body help me.or give some suggestion is it write way to get it or i can try with 2 diffrent query.
thanks in advance
Break your query into two separate queries, one for credit and another for debit and do a full outer join based on descending date, you can order by any column in fact. From the original query, I assumed that VOUCHER_TYPE = 1 is credits and VOUCHER_TYPE = 2 is debit.
Try this (not tested)
with CREDITS as (select TAV.VOUCHER_DATE PDATE,
MLI.DESCRIPTION ACC,
(case when TAV.VOUCHER_TYPE_ID = 1 then SUM(TVL.AMOUNT) else 0 end) CR
from t_acc_voucher tav
join t_voucher_ledger tvl
on tvl.voucher_id = tav.voucher_id
join M_LEDGER_INDEX MLI
on MLI.LEDGER_INDEX_ID = TVL.LEDGER_INDEX_ID
where MLI.VOUCHER_TYPE = 1
group by MLI.DESCRIPTION,
MLI.VOUCHER_TYPE,
TAV.VOUCHER_TYPE_ID,
TAV.VOUCHER_DATE),
debits as (select TAV.VOUCHER_DATE RDATE,
MLI.DESCRIPTION ACC,
(case when TAV.VOUCHER_TYPE_ID = 2 then SUM(TVL.AMOUNT) else 0 end) DR
from T_ACC_VOUCHER TAV
where mli.voucher_type = 2
join t_voucher_ledger tvl
on tvl.voucher_id = tav.voucher_id
join M_LEDGER_INDEX MLI
on MLI.LEDGER_INDEX_ID = TVL.LEDGER_INDEX_ID
group by MLI.DESCRIPTION,
MLI.VOUCHER_TYPE,
TAV.VOUCHER_TYPE_ID,
TAV.VOUCHER_DATE )
select T1.PDATE, T1.ACC, T1.CR, T2.RDATE, T2.ACC, T2.DR
from (select a.*, row_number() over (order by a.PDATE) RN
from credits a) T1
full outer join
select b.*, row_number() over (order by b.RDATE) RN
from debits b) T2
on (t1.rn = t2.rn);
select t1.date1, t1.acc1, t1.cr, t2.date2, t2.acc2, t2.dr
from (the table or query) t1
join (the table or query) t2 on t1.acc1=t2.acc2
where t1.acc1 is not null
we join the same query (or table) twice joinint to itself by acc.
I have a Patient table:
PatientId Admitted
--------- ---------------
1 d/m/yy hh:mm:ss
2 d/m/yy hh:mm:ss
3 d/m/yy hh:mm:ss
I have a PatientMeasurement table (0 to many):
PatientId MeasurementId Recorded Value
--------- ------------- --------------- -----
1 A d/h/yy hh:mm:ss 100
1 A d/h/yy hh:mm:ss 200
1 A d/h/yy hh:mm:ss 300
2 A d/h/yy hh:mm:ss 10
2 A d/h/yy hh:mm:ss 20
1 B d/h/yy hh:mm:ss 1
1 B d/h/yy hh:mm:ss 2
I am trying to create a result set that resembles:
PatientId Numerator Denominator
--------- -------- -----------
1 1 1
2 1 1
3 0 1
Essentially, a patient will have a 1 in the numerator if the have at least one value for measurement A and one value for measurement B. In this example, patient 1 has 3 A measurements and 2 B measures, so the numerator is 1. Patient 2 has 2 A measurements, but no B measurements, so the numerator is 0. Patient has neither an A measurement nor a B measurement, so the numerator is 0.
My query thus far is:
SELECT PatientId, CASE WHEN a.cnt+b.cnt>2 THEN 1 ELSE 0 END Numerator, 1 Denominator
FROM patient p
LEFT OUTER JOIN (
SELECT PatientId, count(*) cnt
FROM PatientMeasurement pm
WHERE MeasurementId='A'
--AND Recorded <= dateadd(hh, 12, Admitted)
GROUP BY PatientId
) a ON p.PatientId=a.PatientId
LEFT OUTER JOIN (
SELECT PatientId, count(*) cnt
FROM PatientMeasurement pm
WHERE MeasurementId='B'
--AND Recorded <= dateadd(hh, 12, Admitted)
GROUP BY PatientId
) b ON p.PatientId=b.PatientId
This works as expected as long as I don't include the correlated, date restriction (Recorded < dateadd(hh, 12, Admitted). Unfortunately, correlating an 'inline view' in this manner is not syntactically valid.
This has forced me to re-write the SQL to:
SELECT PatientId, CASE WHEN v.a+v.b>2 THEN 1 ELSE 0 END Numerator, 1 Denominator
FROM (
SELECT PatientId,
(
SELECT PatientId, count(*) cnt
FROM PatientMeasurement pm
WHERE PatientId=p.PatientId
AND MeasurementId='A'
AND Recorded <= dateadd(hh, 12, Admitted)
GROUP BY PatientId
) a,
(
SELECT PatientId, count(*) cnt
FROM PatientMeasurement pm
WHERE PatientId=p.PatientId
AND MeasurementId='B'
AND Recorded <= dateadd(hh, 12, Admitted)
GROUP BY PatientId
) b
FROM Patient p
) v
My question: Is there a better, more-efficient way to do this?
Thanks for your time.
Try this :
WITH GroupPatients AS
(SELECT MeasurementID, PatientId, Count(*) AS cnt
FROM PatientMeasurement AS pm
INNER JOIN Patient p ON pm.PatientID = p.PatientID
WHERE
MeasurementId IN ('A', 'B')
AND
Recorded <= dateadd(hh, 12, Admitted)
GROUP BY MeasureMentID, PatientId)
SELECT p.PatientID, Case
When IsNull(GPA.cnt, 0) > 0 AND IsNull(GPB.cnt, 0) > 0 Then 1
Else 0
End AS Numerator, 1 AS Denominator
FROM Patient p
LEFT JOIN GroupPatientsA AS GPA ON p.PatientID = GPA.PatientID AND GPA.MeasurementID = 'A'
LEFT JOIN GroupPatientsB AS GPB ON p.PatientID = GPB.PatientID AND GPB.MeasurementID = 'B'
I've made one tweak to the business logic too - your spec says Numerator should be one if a patient has both A and B measurements - however, your clause of a.cnt+b.cnt>2 will erroneously return one if either a.cnt or b.cnt are 3 or more and the other is zero.
Another solution can be close to your original attempt using OUTER APPLY:
SELECT PatientId, CASE WHEN a.cnt+b.cnt>2 THEN 1 ELSE 0 END Numerator, 1 Denominator
FROM patient p
OUTER APPLY (
SELECT count(*) cnt
FROM PatientMeasurement pm
WHERE MeasurementId='A'
AND Recorded <= dateadd(hh, 12, p.Admitted)
AND pm.PatientId = p.PatientId
) AS a(cnt)
OUTER APPLY (
SELECT count(*) cnt
FROM PatientMeasurement pm
WHERE MeasurementId='B'
AND Recorded <= dateadd(hh, 12, p.Admitted)
AND pm.PatientId = p.PatientId
) AS b(cnt)
SELECT p.*,
CASE WHEN
EXISTS
(
SELECT NULL
FROM PatientMeasurement pm
WHERE pm.PatientID = p.ID
AND pm.Type = 'A'
AND pm.Recorded <= DATEADD(hh, 12, p.Admitted)
) AND EXISTS (
SELECT NULL
FROM PatientMeasurement pm
WHERE pm.PatientID = p.ID
AND pm.Type = 'B'
AND pm.Recorded <= DATEADD(hh, 12, p.Admitted)
) THEN 1 ELSE 0 END
FROM Patient p
Assuming you are using Sql 2005 or 2008, the entire query can be simplified using some window functions and a pivot:
with pData as
(
select count(*) over(partition by PatientId, MeasurementId) as cnt,
PatientId, MeasurementId
from PatientMeasurement pm
where MeasurementId in('A','B')
and Recorded <= dateadd(hh, 12, Admitted)
)
select PatientId, coalesce([A],0) as cntA, coalesce([B],0) as cntB,
case when coalesce([A],0) + coalesce([B],0) > 2 then 1 else 0 end as Numerator,
1 as Denominator
from pData
pivot (max(cnt) for MeasurementId in([A],[B])) pvt
DECLARE #TimeSlot int;
SET #TimeSlot = 12;
WITH
pt AS (
SELECT p.PatientID, p.Admitted, m.MeasurementID, m.Recorded,
CASE
WHEN m.Recorded <= dateadd(hh, #TimeSlot, p.Admitted) THEN 1
ELSE 0
END AS "InTimeSlot"
FROM Patient AS p
LEFT JOIN PatientMeasurement AS m ON p.PatientID = m.PatientID
),
cntA AS (
SELECT PatientID, count(*) AS "A_count"
FROM pt WHERE MeasurementID='A' AND InTimeSlot = 1
GROUP BY PatientID
),
cntB AS (
SELECT PatientID, count(*) AS "B_count"
FROM pt WHERE MeasurementID='B' AND InTimeSlot = 1
GROUP BY PatientID
),
cntAB AS (
SELECT p.PatientID
,coalesce(a.A_count, 0) AS "A_cnt"
,coalesce(b.B_count, 0) AS "B_cnt"
FROM Patient as p
LEFT JOIN cntA AS a ON p.PatientID = a.PatientID
LEFT JOIN cntB AS b ON p.PatientID = b.PatientID
),
cntN AS (
SELECT PatientID,
CASE WHEN A_cnt > 0 AND B_cnt > 0 THEN 1 ELSE 0 END AS Numerator
FROM cntAB
)
SELECT PatientID, Numerator, 1 AS Denominator FROM cntN