How to get rid of repeated rows? - sql-server-2008

This is the tables structure I have:
1) When I use this query:
SELECT T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.StatusID
--,case when (dbo.T_ActionTicketLog.StatusID <> 2 and dbo.T_ActionTicketLog.StatusID <> 6) then dbo.T_TicketPrint.TicketBarCode
--else null end as TicketBarCode
FROM T_ActionTicketLog
LEFT OUTER JOIN T_OrderTicket ON T_ActionTicketLog.TicketOrderID = T_OrderTicket.ID
--LEFT OUTER JOIN T_TicketPrint ON T_OrderTicket.ActionTicketID = T_TicketPrint.ActionTicketID and T_OrderTicket.ID = T_TicketPrint.OrderTicketID
where T_ActionTicketLog.ActionTicketID = 24014999
Everything works fine and looks as following:
2) But, when I use this query because I need T_TicketPrint.TicketBarCode:
SELECT T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.StatusID
,case when (dbo.T_ActionTicketLog.StatusID <> 2 and dbo.T_ActionTicketLog.StatusID <> 6) then dbo.T_TicketPrint.TicketBarCode
else null end as TicketBarCode
FROM T_ActionTicketLog
LEFT OUTER JOIN T_OrderTicket ON T_ActionTicketLog.TicketOrderID = T_OrderTicket.ID
LEFT OUTER JOIN T_TicketPrint ON T_OrderTicket.ActionTicketID = T_TicketPrint.ActionTicketID and T_OrderTicket.ID = T_TicketPrint.OrderTicketID
where T_ActionTicketLog.ActionTicketID = 24014999
I've got the extra rows and it looks as following:
Here we have the sold tickets in T_TicketPrint table. When the ticket is sold (status 3) it can be printed out (status 10). The important thing is when we print the ticket, every time new bar code is generated. In this case we have a ticket that was sold and printed out 3 times with 3 different bar codes.
The desired result (other values in TicketBarCode column should be NULL):
How to get rid of extra rows?
This is final correct script:
;WITH cte1
AS (SELECT T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.TicketOrderID, T_ActionTicketLog.StatusID, T_ActionTicketLog.Created,
CASE WHEN StatusID IN (3, 10) THEN 1
ELSE 0
END
* ROW_NUMBER() OVER ( PARTITION BY T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.StatusID ORDER BY T_ActionTicketLog.Created ) AS rn
FROM T_ActionTicketLog
LEFT OUTER JOIN T_OrderTicket ON T_ActionTicketLog.TicketOrderID = T_OrderTicket.ID
),
cte2
AS ( SELECT ActionTicketID , OrderTicketID, TicketBarCode,
ROW_NUMBER() OVER ( PARTITION BY ActionTicketID ORDER BY Created ) AS rn
FROM T_TicketPrint
)
SELECT c1.ActionTicketID, c1.StatusID , oa.TicketBarCode
FROM cte1 c1
OUTER APPLY ( SELECT *
FROM cte2 c2
WHERE c1.ActionTicketID = c2.ActionTicketID AND c1.TicketOrderID = c2.OrderTicketID AND c1.rn = c2.rn
) oa
where c1.ActionTicketID = 24014999
order by c1.Created

If I got you right you need to apply to first pair of(3, 10) the first barcode, second barcode to second pair and so on. But it is not clear which of these pairs is first or second.
This is test script:
DECLARE #actions TABLE
(
TicketID INT ,
StatusID INT
)
DECLARE #prints TABLE
(
TicketID INT ,
BarCode NVARCHAR(MAX)
)
INSERT INTO #actions
VALUES ( 1, 2 ),
( 1, 4 ),
( 1, 3 ),
( 1, 10 ),
( 1, 6 ),
( 1, 3 ),
( 1, 10 ),
( 1, 6 ),
( 1, 3 ),
( 1, 10 ),
( 1, 6 ),
( 1, 7 )
INSERT INTO #prints
VALUES ( 1, 'C1' ),
( 1, 'C2' ),
( 1, 'C3' );
WITH cte1
AS ( SELECT * ,
CASE WHEN StatusID IN ( 3, 10 ) THEN 1
ELSE 0
END
* ROW_NUMBER() OVER ( PARTITION BY TicketID, StatusID ORDER BY StatusID ) AS rn
FROM #actions a
),
cte2
AS ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY TicketID ORDER BY BarCode ) AS rn
FROM #prints a
)
SELECT c1.TicketID ,
c1.StatusID ,
oa.BarCode
FROM cte1 c1
OUTER APPLY ( SELECT *
FROM cte2 c2
WHERE c1.TicketID = c2.TicketID
AND c1.rn = c2.rn
) oa
Ouptut:
TicketID StatusID BarCode
1 2 NULL
1 3 C1
1 3 C2
1 3 C3
1 4 NULL
1 6 NULL
1 6 NULL
1 6 NULL
1 7 NULL
1 10 C1
1 10 C2
1 10 C3
Something like:
;WITH cte1
AS ( SELECT T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.OrderTicketID, T_ActionTicketLog.StatusID ,
CASE WHEN StatusID IN ( 3, 10 ) THEN 1
ELSE 0
END
* ROW_NUMBER() OVER ( PARTITION BY T_ActionTicketLog.ActionTicketID, T_ActionTicketLog.StatusID ORDER BY T_ActionTicketLog.Created ) AS rn
FROM T_ActionTicketLog
LEFT OUTER JOIN T_OrderTicket ON T_ActionTicketLog.TicketOrderID = T_OrderTicket.ID
),
cte2
AS ( SELECT ActionTicketID , OrderTicketID, TicketBarCode
ROW_NUMBER() OVER ( PARTITION BY ActionTicketID ORDER BY Created ) AS rn
FROM T_TicketPrint
)
SELECT c1.ActionTicketID, c1.StatusID , oa.TicketBarCode
FROM cte1 c1
OUTER APPLY ( SELECT *
FROM cte2 c2
WHERE c1.ActionTicketID = c2.ActionTicketID AND c1.ID = c2.OrderTicketID AND c1.rn = c2.rn
) oa

Related

Mysql - Access column from grandparent's joined table in WHERE in subquery inside parent's FROM

Inside a WHERE inside a subquery inside a FROM inside another subquery inside a SELECT that's joined to another table, I need to access a column from that joined table.
edited to add more complete example:
SELECT
field_one,
field_two,
field_three,
field_one-field_three AS field_five,
field_six
FROM (
SELECT
IFNULL(
(
SELECT
SUM(us.field_seven) AS field_one
FROM
table_one us
WHERE
us.rto_id = rto.relevant_field_one
AND
us.created >= (
SELECT
IF(
selected_date IS NULL,
MIN(created),
selected_date
)
FROM (
SELECT
IF(
latest_date < DATE_SUB(CURDATE(), INTERVAL rtt.relevant_field_two DAY),
CURDATE(),
MAX(prevdate)
) AS selected_date,
created
FROM (
SELECT
created,
#calc_prevdate as prevdate,
DATEDIFF(#calc_prevdate, created) AS diff,
#calc_prevdate := created
FROM (
SELECT
sto.created
FROM
table_one sto
WHERE
sto.rto_id = rto.relevant_field_one
UNION ALL
SELECT
stt.created
FROM
table_two stt
WHERE
stt.rto_id = rto.relevant_field_one
ORDER BY
created DESC
) AS x
CROSS JOIN (
SELECT
#calc_prevdate := NULL
) as vars
) AS z
CROSS JOIN (
SELECT
MAX(created) AS latest_date
FROM(
SELECT
sto.created
FROM
table_one sto
WHERE
sto.rto_id = rto.relevant_field_one
UNION ALL
SELECT
stt.created
FROM
table_two stt
WHERE
stt.rto_id = rto.relevant_field_one
ORDER BY
created DESC
) as z
) AS y
WHERE
diff > rtt.relevant_field_two
) as w
)
GROUP BY us.rto_id
),0
) AS field_one,
IFNULL(
(
SELECT
SUM(tt.field_seven) AS field_three
FROM
table_two tt
WHERE
tt.rto_id = rto.relevant_field_one
AND
tt.created >= (
SELECT
IF(
selected_date IS NULL,
MIN(created),
selected_date
)
FROM (
SELECT
IF(
latest_date < DATE_SUB(CURDATE(), INTERVAL rtt.relevant_field_two DAY),
CURDATE(),
MAX(prevdate)
) AS selected_date,
created
FROM (
SELECT
created,
#calc_prevdate as prevdate,
DATEDIFF(#calc_prevdate, created) AS diff,
#calc_prevdate := created
FROM (
SELECT
sto.created
FROM
table_one sto
WHERE
sto.rto_id = rto.relevant_field_one
UNION ALL
SELECT
stt.created
FROM
table_two stt
WHERE
stt.rto_id = rto.relevant_field_one
ORDER BY
created DESC
) AS x
CROSS JOIN (
SELECT
#calc_prevdate := NULL
) as vars
) AS z
CROSS JOIN (
SELECT
MAX(created) AS latest_date
FROM(
SELECT
sto.created
FROM
table_one sto
WHERE
sto.rto_id = rto.relevant_field_one
UNION ALL
SELECT
stt.created
FROM
table_two stt
WHERE
stt.rto_id = rto.relevant_field_one
ORDER BY
created DESC
) as z
) AS y
WHERE
diff > rtt.relevant_field_two
) as w
)
GROUP BY tt.rto_id
), 0
) AS field_three,
IFNULL(
(
SELECT
COUNT(*) AS field_two
FROM
table_two tt
WHERE
tt.rto_id = rto.relevant_field_one
GROUP BY tt.rto_id
), 0
) AS field_two,
IFNULL(
(
SELECT
GREATEST(
IFNULL(MAX(us.created), 0), IFNULL(MAX(tt.created), 0)
) AS field_six
FROM
table_one us
LEFT JOIN
table_two tt ON us.rto_id = tt.rto_id
WHERE
us.rto_id = rto.relevant_field_one
GROUP BY us.rto_id
), 0
) AS field_six
FROM
relevant_table_one rto
LEFT JOIN
relevant_table_two rtt ON rto.rtt_id = rtt.id
WHERE
rto.rtt_id = ?
GROUP BY rto.relevant_field_one
) v
ORDER BY id ASC;
given that query, I need to access relevant_table_one.relevant_field_one and relevant_table_two.relevant_field_two from inside the subqueries, but the restrictions on subqueries dictates that you cant access a parents table in a subquery inside a FROM
I managed to solve this (so far I think) by adding #rfo := relevant_field_one and #rft := relevant_field_two up in the select where they were accessable and then referring to the created variables instead of the columns down in the nested query where relevant.
It's possible I'm just getting false positives but so far the solution appears to be working.

Operand should contain 1 column(s) - in mysql query

I have the following complex query that is giving me an error
Operand should contain 1 column(s)
Can anyone suggest what is wrong
SELECT
t.user_id AS user_id,
t.organisation_id AS organisation_id,
t.firstname AS firstname,
t.surname AS surname,
t.username AS username,
t.year_id AS year_id,
t.form_name AS form_name,
t.House AS House,
rcPoints.total AS milestoneRedeemedCodesTotal,
rcFilteredPoints.total AS redeemedCodesTotalFiltered,
(
COALESCE (rcFilteredPoints.total, 0) - COALESCE (milestoneHistory.total, 0)
) AS redeemedCodesTotalAvailableFiltered,
ABS(
FLOOR(
(
COALESCE (rcFilteredPoints.total, 0) - COALESCE (milestoneHistory.total, 0)
) / 1000
) * 1000
) AS redeemedCodesTotalTowardsMilestone,
ABS(
FLOOR(
(
COALESCE (rcFilteredPoints.total, 0) - COALESCE (milestoneHistory.total, 0)
) / 1000
)
) AS redeemedCodesMilestoneTriggers,
COALESCE (milestoneHistory.total, 0) AS historyTotal
FROM
`myuser` `t`
LEFT JOIN (
SELECT
rc.user_id AS user_id,
SUM(rc.school_points) AS total
FROM
`redeemed_codes` `rc`
INNER JOIN myuser m ON (m.user_id = rc.user_id)
WHERE
(rc.date_redeemed >= 0)
AND (m.organisation_id = 58022)
GROUP BY
rc.user_id
) AS rcPoints ON (rcPoints.user_id = t.user_id)
LEFT JOIN (
SELECT
rc.user_id AS user_id,
SUM(rc.school_points) AS total
FROM
`redeemed_codes` `rc`
INNER JOIN myuser m ON (m.user_id = rc.user_id)
WHERE
(rc.date_redeemed >= 0)
AND (m.organisation_id = 58022)
GROUP BY
rc.user_id
) AS rcFilteredPoints ON (
rcFilteredPoints.user_id = t.user_id
)
LEFT JOIN (
SELECT
mh.user_id AS user_id,
mh.milestone_id AS milestone_id,
MAX(mh.points_when_triggered) AS total
FROM
`milestone_history` `mh`
WHERE
mh.milestone_id = 13
GROUP BY
mh.user_id
) AS milestoneHistory ON (
milestoneHistory.user_id = t.user_id
)
WHERE
(
(
SELECT
COALESCE (count(*), 0)
FROM
milestone_history mha
WHERE
mha.milestone_id = 13
AND mha.user_id = t.user_id
) = 0
)
AND (t.organisation_id = 58022)
AND
(
SELECT * FROM
redeemed_codes t1
WHERE
organisation_id = 1
AND
(
SELECT
sum(school_points)
FROM
redeemed_codes t2
WHERE
t2.redeemed_code_id <= t1.redeemed_code_id
) >= 1000
ORDER BY redeemed_code_id
LIMIT 1
)
GROUP BY
t.user_id
ORDER BY
redeemedCodesMilestoneTriggers DESC
LIMIT 1
Your query might have multiple errors, but this condition in the WHERE clause is definitely suspect and would lead to that error:
AND (SELECT *
FROM redeemed_codes t1
WHERE organisation_id = 1 AND
(SELECT sum(school_points)
FROM redeemed_codes t2
WHERE t2.redeemed_code_id <= t1.redeemed_code_id
) >= 1000
ORDER BY redeemed_code_id
LIMIT 1
)
I have no idea what you are trying to do. Sometimes, the solution is simply EXISTS:
EXISTS (SELECT *
FROM redeemed_codes t1
WHERE organisation_id = 1 AND
(SELECT sum(school_points)
FROM redeemed_codes t2
WHERE t2.redeemed_code_id <= t1.redeemed_code_id
) >= 1000
)

How to set previous row's column value?

I use MS SQL Server 2008 R2.
Is it possible to set the value of BarCode column in row with StatusID = 3 to the same value in row with BarCode with StatusID = 10.
Try this:
DECLARE #t TABLE
(
StatusID INT ,
UserID INT ,
BarCode CHAR(10)
)
INSERT INTO #t
VALUES ( 1, 3378, '-1' ),
( 2, 3378, '-1' ),
( 10, 3378, 'some_code1' ),
( 1, 3379, '-1' ),
( 3, 3379, '-1' ),
( 10, 3379, 'some_code2' );
WITH cte
AS ( SELECT StatusID ,
UserID ,
BarCode ,
ROW_NUMBER() OVER ( PARTITION BY UserID ORDER BY StatusID ) AS rn
FROM #t
)
UPDATE c1
SET BarCode = c2.BarCode
FROM cte c1
JOIN cte c2 ON c1.UserID = c2.UserID
AND c1.rn + 1 = c2.rn
WHERE c2.StatusID = 10
AND c1.StatusID = 3
SELECT * FROM #t
Output:
StatusID UserID BarCode
1 3378 -1
2 3378 -1
10 3378 some_code1
1 3379 -1
3 3379 some_code2
10 3379 some_code2

Join on max(T.<column>) including further information of T

I have two tables
create table item( id int )
insert into item ( id ) values ( 1 ), ( 2 ), ( 3 )
create table itemstatus
(
itemid int
, ts datetime
, "status" int
)
insert into itemstatus ( itemid, ts, status ) values
( 1, '2013-12-01T12:00:00.000', 1 ),
( 1, '2013-12-01T11:00:00.000', 2 ),
( 1, '2014-01-01T12:00:00.000', 1 ),
( 2, '2011-01-01T12:00:00.000', 1 )
I'd like to get all items with the last status set, in this case
1, '2014-01-01T12:00:00.000', 1
2, '2011-01-01T12:00:00.000', 1
3, NULL, NULL
What's the most efficient way to solve this?
I tried with a subselect and I get the latest timestamp, but I'm not able to add the status since this field is not included in aggregate-function or group-by. If I add it, the results got grouped by status - logically - but that leads to the fact, that I get too much result-lines and would have to add a further condition / subselect.
You may use the Fiddle-link for created tables and testdata. The second query includes the status-field.
Edit:
adding a further join does the trick, but I doubt that's the way to do it.
select
i.*
, d.*
, s.status
from
item i
left join ( select ts = max(ts), itemid from itemstatus group by itemid ) d
on 1 = 1
and i.id = d.itemid
left join itemstatus s
on 1 = 1
and s.itemid = d.itemid
and s.ts = d.ts
See SQL-fiddle for testing.
You can use row_number partitioned by itemid and ordered by ts desc to get the latest registration in itemstatus per itemid.
select I.id,
S.ts,
S.status
from item as I
left outer join (
select S.status,
S.ts,
S.itemid,
row_number() over(partition by S.itemid
order by S.ts desc) as rn
from itemstatus as S
) as S
on I.id = S.itemid and
S.rn = 1

check 2 primary key in one table

I have table psc_Pro_ProfessorPositions(ProfessorID,PositionID,StartDate,EndDate). It have 2 primary key is ProfessorID,PositionID.
I want to check ProfessorID,PositionID not in table to insert.I wrote like this:
insert into CoreUIs.dbo.psc_Pro_ProfessorPositions
(
ProfessorID,PositionID,StartDate,EndDate
)
select a.MaQuanLy,b.MaQuanLy,convert(smalldatetime,NgayHieuLuc),convert(smalldatetime,NgayHetHieuLuc)
from inserted
inner join GiangVien a on a.MaGiangVien = inserted.MaGiangVien
inner join ChucVu b on b.MaChucVu = inserted.MaChucVu
where a.MaQuanLy not in (select ProfessorID from CoreUIs.dbo.psc_Pro_ProfessorPositions)
and b.MaQuanLy not in (select PositionID from CoreUIs.dbo.psc_Pro_ProfessorPositions)
But it's wrong.Can help me?Thanks all.
;WITH x AS
(
SELECT TeacherID, ClassID, ClassStuID, s = [SUM],
rn = ROW_NUMBER() OVER (PARTITION BY TeacherID ORDER BY ClassID)
FROM dbo.TB1
)
SELECT TeacherID, ClassID, ClassStuID,
[SUM] = CASE rn WHEN 1 THEN s ELSE NULL END
FROM x
ORDER BY TeacherID, [SUM] DESC;
You can employ CTEs with ROW_NUMBER()OVER() to identify the first row of each TeacherID:
; with a as (
select * from TB1
union
select * from TB2
)
, b as (
select *, r=ROW_NUMBER()over(partition by a.TeacherID order by a.TeacherID,
a.ClassID, a.ClassStuID) from a
)
select b.TeacherID, b.ClassID, b.ClassStuID
, [SUM]=case b.r when 1 then b.[SUM] else null end
from b
order by b.TeacherID, b.r
go
Result: