check 2 primary key in one table - sql-server-2008

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:

Related

Sql query on taking the value from a case within select statement in a CTE

I am trying to write a SQL. In this, I want columns Bank, and Y-Bank in the output. Y-Bank is being calculated based on certain case statements, using CTE, but I am not able to return Y-Plant as the column.
I also created a table as shown in the input/output here:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=749e4ca1570880e9c64c4553d18dea1a
Below is the code:
WITH CTE AS
(
SELECT
"BANK",
(select
case
when "TEST"=0 AND "TEST1">0
THEN ( SELECT COUNT("ZONES")FROM mytable I WHERE I.BANK = O.BANK AND I."ZONES"='Y' )
END AS "Y-BANK"
from(
(SELECT
CASE WHEN ( (SELECT COUNT("ZONES")FROM mytable I WHERE I.BINDER = O.BINDER AND I."ZONES"='N' ) = 0 ) AND ( (SELECT COUNT("ZONES")FROM mytable I WHERE I.BINDER = O.BINDER AND I."ZONES"='Y' ) > 0 )
THEN ( SELECT COUNT("ZONES")FROM mytable I WHERE I."TOTAL LINE"= O."TOTAL LINE"AND I."ZONES"='N' )
END AS "TEST",
CASE WHEN ( (SELECT COUNT("ZONES")FROM mytable I WHERE I.BINDER = O.BINDER AND I."ZONES"='' ) = 0 ) AND ( (SELECT COUNT("ZONES")FROM mytable I WHERE I.BINDER = O.BINDER AND I."ZONES"='Y' ) > 0 )
THEN ( SELECT COUNT("ZONES")FROM mytable I WHERE I."TOTAL LINE" = O."TOTAL LINE" AND I."ZONES"='Y' )
END AS "TEST1"
from mytable )
)
)
FROM mytable O
)
SELECT *
FROM CTE O
Can someone help me out on how can I correct it?
If I understand correctly, you want to count the number of "Y" values for each bank. You can use a window function:
select t1.*, sum(zones = 'Y') over (partition by Bank1) as y_bank
from t1
Here is a db<>fiddle.
you can use following query
select
bank1 as "bank",
(select count(*)over(partition by Bank1,Zones order by Bank1 desc) as "y_bank" from t1 where Zones = 'Y' and Bank1 = t.Bank1 limit 1) as "y_bank"
from t1 t

Group by date and take the last one

This is my table :
What I'm trying to do, is to take the last disponibility of a user, by caserne. Example, I should have this result :
id id_user id_caserne id_dispo created_at
31 21 12 1 2019-10-24 01:21:46
33 21 13 1 2019-10-23 20:17:21
I've tried this sql, but it does not seems to work all the times :
SELECT * FROM
( SELECT id, id_dispo, id_user, id_caserne, MAX(created_at)
FROM disponibilites GROUP BY id_user, id_caserne, id_dispo
ORDER BY created_at desc ) AS sub
GROUP BY id_user, id_caserne
What am I doing wrong ?
I would simply use filtering in the where clause using a correlated subquery:
select d.*
from disponibilites d
where d.created_at = (select max(d2.created_at)
from disponibilites d2
where d2.id_user = d.id_user
);
EDIT:
Based on your comments:
select d.*
from disponibilites d
where d.created_at = (select max(d2.created_at)
from disponibilites d2
where d2.id_user = d.id_user and
d2.id_caserne = d.id_caserne
where date(d2.created_at) = date(d.created_at)
);
You can use a correlated subquery, as demonstrated by Gordon Linoff, or a window function if your RDBMS supports it:
select * from (
select
t.*,
rank() over(partition by id_caserne, id_user order by created_at desc) rn
from disponibilites t
) x
where rn = 1
Another option is to use a correlated subquery without aggregation, only with a sort and limit:
select *
from mytable t
where created_at = (
select created_at
from mytable t1
where t1.id_user = t.id_user and t1.id_caserne = t.id_caserne
order by created_at desc
limit 1
)
With an index on (id_user, id_caserne, created_at), this should be a very efficient option.
you can join your max(created_date) to your original table
select t1.* from disponibilites t1
inner join
(select max(created_at), id_caserne, id
from disponibilites
group by id_caserne, id) t2
on t2.id = t1.id

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.

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

Finding the most common entry in a column - SQL

I have a table called MyTable like so
A B
101 Dog
209 Cat
209 Cat
209 Dog
193 Cow
193 Dog
101 Dog
193 Dog
193 Cow
And I want to pull out the most common B for each A so it would end up being like this (note that there can be ties)
A B
101 Dog
209 Cat
193 Dog
193 Cow
How could I write sql to do this?
Alternatively, you can use HAVING clause instead of JOIN.
SELECT A, B
FROM table1 o
GROUP BY A, B
HAVING COUNT(*) =
(
SELECT MAX(totalCOunt)
FROM
(
SELECT A, B, COUNT(*) totalCount
FROM table1
GROUP BY A,B
) x
WHERE o.A = x.A
GROUP BY x.A
)
SQLFiddle Demo
You could use a filtering join to list the (A,B) combination with the highest rowcount:
select src.*
from (
select A
, B
, count(*) cnt
from YourTable
group by
A
, B
) src
join (
select A
, max(cnt) as maxcnt
from (
select A
, B
, count(*) cnt
from YourTable
group by
A
, B
) comb
group by
A
) maxab
on maxab.A = src.A
and maxab.maxcnt = src.cnt
Example at SQL Fiddle.
If your database supports windowing functions, you can use dense_rank(), like:
select *
from (
select dense_rank() over (
partition by A
order by cnt desc) as rn
, *
from (
select A
, B
, count(*) cnt
from YourTable
group by
A
, B
) t1
) t2
where rn = 1
Window function example at SQL Fiddle. Windowing functions are available on recent versions of SQL Server, Oracle and PostgeSQL.
select g3.A,g3.B
from
(
select A,Max(C) MC
from
(
select A,B,count(*) C
from (<your entire select query>) tbl
group by A,B
) g1
group by A
) g2
join
(
select A,B,count(*) C
from (<your entire select query>) tbl
group by A,B
) g3 on g2.A=G3.A and g3.C=g2.MaxC
SQL FIDDLE Example
select
A, B
from
(
select
A, B, row_number() over (partition by A order by cnt desc) as RowNum
from
(
select
T.A, T.B, count(*) over (partition by T.A, T.B) as cnt
from T
) as A
) as B
where RowNum = 1