How i can solve query like that - sql-server

I have one problem in sql server.I have some records like
PKId EquipmentID StudentId Issueddate Returneddate
1 116 230 2014-01-14 17:14:58.940 2014-01-18 14:12:038.876
2 116 237 2014-01-14 17:14:58.940 NULL
3 117 400 2014-01-14 17:14:58.940 2014-01-18 14:12:038.876
Here EquipmentID and StudentId are the foreign key.We need to write a query which gives us data based on Returneddate.We need to check condition one like in first row equipmentid is 116 and it's assigned to studentId 230 where Returneddate is not null means for now this 116 equipment is free means it is unassiged but in the next line same equipment is assigned to student 237 and hiis returneddate is NULL means this equipment is assined to someone and in last case equipmentid 117 assigned to studentid 400 and it's returneddate is not null means it is available.
So we need to show only those records which is available.If i took this case as example then it will give me
3 117 400 2014-01-14 17:14:58.940 2014-01-18 14:12:038.876 row as a output.
I Tried:
select * from Equipment where (EquipmentID not in (select EquipmentID
from PEquip where Returneddate is null))
Please help me.
Hope you understand.

If I've got it right you need EquipmentID for which there are no rows with NULL Returneddate.
SELECT * FROM T as T1
WHERE NOT EXISTS (SELECT * FROM T
WHERE EquipmentID=T1.EquipmentID
AND Returneddate IS NULL)

select * from EuipmentDetails where EquipmentID
not in (select EquipmentID from EuipmentDetails where Returneddate is NULL )

Try this:
select * from theTable t1 where returneddate =
(select max Issueddate from theTable t2 where t1.EquipmentId = t2.EquipmentId)
and returneddate is not NULL;

Assuming that the order of PkId is consistent with the chronology of the data, I'd go with
SELECT PKId, EquipmentID, StudentId, Issueddate, Returneddate
FROM Equipment E
INNER JOIN (SELECT MAX(PKId) AS PKId FROM Equipment GROUP BY EquipmentId) AS T
ON E.PKId = T.PKId
WHERE E.ReturnedDate IS NOT NULL
Logic: select the latest entry for each EquipmentId, IF the latest entry has ReturnedDate not null.

Related

SQL Join between timestamps

I have two tables like these:
Table1
Timestamp
value
2022-07-04 16:16:50
120
2022-07-04 16:17:25
110
2022-07-04 16:17:35
105
2022-07-04 16:17:45
130
Table2
Begin_Timestamp
End_Timestamp
2022-07-04 16:14:00
2022-07-04 16:17:02
2022-07-04 16:17:11
2022-07-04 16:17:30
2022-07-04 16:17:38
2022-07-04 16:17:47
I want to join the two tables based on their timestamp. So if the table1.timestamp is between table2.Begin_timestamp and table2.End_timestamp, it would add "isFound=1". For example, the first row of Table1 is 16:16:50 and is between 16:14:00 and 16:17:02 (the first row of Table2). I want a table like this:
Wanted table
Timestamp
value
isFound
2022-07-04 16:16:50
120
1
2022-07-04 16:17:25
110
1
2022-07-04 16:17:35
105
0
2022-07-04 16:17:45
130
1
I tried the following but it only works for the last row.
SELECT t1.timestamp,
t1.value,
(CASE WHEN (t1.Timestamp BETWEEN t2.Begin_Timestamp AND t2.End_Timestamp)
THEN '1'
ELSE '0') AS isFound
FROM Table1 t1
LEFT JOIN (
SELECT *
FROM Table2
) AS t2
ON t1.Timestamp BETWEEN t2.Begin_Timestamp AND t2.End_Timestamp
)
RESULT :
Timestamp
value
isFound
2022-07-04 16:16:50
120
NULL
2022-07-04 16:17:25
110
NULL
2022-07-04 16:17:35
105
NULL
2022-07-04 16:17:45
130
1
I use MS-SQL server. Can someone help me with that? I feel like this it is easy but I don't find the problem. Thanks!
You can do a LEFT JOIN between the two tables on the conditions that the "Table1.Timestamp" is found between the two "Table2" timestamps.
Then you can exploit the non-matching rows having both "Begin_Timestamp" and "End_Timestamp" equal to NULL inside your CASE statement for the creation of the "isFound" field.
SELECT table1.*,
CASE WHEN [Begin_Timestamp] IS NULL THEN 0 ELSE 1 END AS isFound
FROM table1
LEFT JOIN table2
ON table2.[Begin_Timestamp] < table1.[Timestamp]
AND table2.[End_Timestamp] > table1.[Timestamp]
Check the demo here.
Note: this solution assumes that all your "table2" timestamp periods are disjoint among each other, otherwise you need to add the DISTINCT keyword right after the SELECT keyword.
You can use case expression combined with an exists check:
select *,
case when exists (
select * from t2
where t1.Timestamp between t2.Begin_Timestamp and t2.End_Timestamp
) then 1 else 0 end IsFound
from t1;

SQL Check to see if any value in a list exists in another table

I have a temp table that looks something like this:
Record DepartmentId PositionId EmployeeId StatusId CustomerId
1 Null Null Null 4
2 7 454 Null Null
3 Null 454 Null 3
3 Null Null Null Null 214
3 Null Null Null Null 100
3 Null Null Null Null 312
4 Null Null Null Null 357
I inserted the above into the temp table from tables that looked like this:
Record Table Record-to-Department Record-to-Position
Record Name Record DepartmentId Record PositionId
1 Red 2 7 2 454
2 blue 3 454
3 Green
4 Purple
Record-To-Status Record-To-Customer
Record StatusId Record CustomerId
1 4 3 214
3 3 3 100
3 312
4 357
I have an employee whose record looks something like this:
EmployeeId DepartmentId PositionId StatusId
342 7 454 4
Employee Customers:
EmployeeId CustomerId
342 357
342 95
342 720
In this scenario, it would return Record 1 (because it matches the StatusId), Record 2 (because it matches both the DepartmentId and the PositionId), but it would not return Record 3 because it only matches the PositionId and not the StatusId, and it would return RecordId 4 because one of the Employee CustomerIds matches the CustomerId on Record 4.
I got part of this answer on another question enter link description here (please forgive me I am new and trying to figure out how to ask everything I need to know), but I can't figure out how to handle the multi-records.
I tried selecting the Employees customer Id's into a table variable and then attempted to use the Coalesce like this:
Declare #Customers table(CustomerId int)
INSERT INTO #Customers(CustomerId)
SELECT DISTINCT S.CustomerId
FROM employee_Customers
Select * from tbl
WHERE
COAlesce(StatusId,#StatusId)=#StatusId AND
COALESCE(DepartmentId,#DepartmentId)=#DepartmentId AND
Coalesce(PositionId,#PositionId)=#PositionId AND
Coalesce(EmployeeCompanyId,#EmployeeCompanyId) = #EmployeeCompanyId AND
COALESCE((Select CustomerId from tbl_Requirement_to_Customer),(Select CustomerId from #Customers)) = (Select CustomerId from #Customers)
But I receive the error "Subquery Returned more than 1 value".
I have a possible solution you can try. I don't think it will be plug-and-play but hopefully you can adapt it to your situation. I am using just the data as presented in your temp table, your employee record and your Employee-customers correlation.
The basic logic is to join your temp table to the employee(s) using or condition, but then to get a count of populated values, and compare this count to a count of the number of matching values, which must be at least the first count and greater than zero.
This returns your desired output:
select t.*
from Temp t
left join emp e on e.DepartmentId=t.DepartmentId or e.PositionId=t.PositionId or e.EmployeeId=t.EmployeeId or e.StatusId=t.StatusId
outer apply (
select case when exists (
select * from EmployeeCustomers ec join emp e on e.EmployeeId=ec.EmployeeId where ec.CustomerID=t.CustomerId
) then 1 else 0 end CustomerIdMatch
)c
outer apply (
values (
Iif(t.departmentId is null,0,1) +
Iif(t.PositionId is null,0,1) +
Iif(t.EmployeeId is null,0,1) +
Iif(t.StatusId is null,0,1) +
c.CustomerIdMatch
))x(Cnt)
outer apply (
values (
Iif(t.departmentId=e.DepartmentId,1,0) +
Iif(t.PositionId=e.PositionId,1,0) +
Iif(t.EmployeeId=e.EmployeeId,1,0) +
Iif(t.StatusId=e.StatusId,1,0) +
c.CustomerIdMatch
))y(Cnt2)
where cnt2>=cnt and cnt2>0
See working DB<>Fiddle

SQL Server: how to populate ID to table using an ID from another table?

I have TaxTable with primary key taxid
the structure is as follow
taxid Type noteID
1 A
2 G
3 G
4 G
I also have a noteTable the table looks like this
NoteID SNoteText
456 Hellow joe
457 Non-Taxable
458 Non-Taxable
459 Non-Taxable
Now I need to populate taxType=G with noteID that Snotetext=Non-Taxable
so the end result will be like follow
taxid Type noteID
1 A
2 G 457
3 G 458
4 G 459
The order does not really matter, 457 can be in taxID 2 or 3 or 4. I mean there is no link between noteID 457 and TaxID 2 , it can be anywhere.
The important key to know is that the total count of Snotetext="Non-Taxable" will always be the same as taxType=G. So in this example, there is 3 rows of TaxType=G and there is 3 rows of sNoteText="Non-Taxable" and this is important.
I hope that make sense. Thank you for the help
If you don't have a link between tables then create it. Something like this:
create table #taxTable(taxid int, [type] char(1),noteID int)
insert #taxTable(taxid,[type])
values (1,'A'),(2,'G'),(3,'G'),(4,'G'),
(5,'A'),(6,'G'),(7,'G'),(8,'G'),(9,'G')
create table #noteTable(NoteID int,SNoteText varchar(50))
insert #noteTable
values
(456, 'Hellow joe'),
(457 ,'Non-Taxable'),
(458 ,'Non-Taxable'),
(459 ,'Non-Taxable')
declare #cnt int
select #cnt=COUNT(noteid) from #noteTable where SNoteText='Non-Taxable'
-- It is possible to add 3rd CTE for cnt
;with n as (select row_number() over(order by noteID) rn,
noteId
from #noteTable where SNoteText='Non-Taxable'),
t as (select row_number() over(order by taxid) rn,
taxid,[type], noteid from #taxtable where [type]='G')
update t
set noteID = n.noteid
from n inner join t on n.rn= (t.rn%#cnt)+1
select * from #taxtable

Query trick - kind of unpivot

I have the following table
SnapShotDay OperationalUnitNumber IsOpen StatusDate
1-01-2014 001 1 1-01-2014
2-01-2014 NULL NULL NULL
3-01-2014 001 0 3-01-2014
4-01-2014 NULL NULL NULL
5-01-2014 001 1 5-01-2014
I obtain this with a SELECT construct, but what I need to do now is fill in the "NULL"ed rows by taking values from the first Non nulled row before. The latter would give:
SnapShotDay OperationalUnitNumber IsOpen StatusDate
1-01-2014 001 1 1-01-2014
2-01-2014 001 1 1-01-2014
3-01-2014 001 0 3-01-2014
4-01-2014 001 0 3-01-2014
5-01-2014 001 1 5-01-2014
In functional words: I have events records that give me an event on a date for an oprrational unit; the event is: IsOpen or IsClosed. Chaining those events together according to the date gives a sort of Ranges. What I need is generate daily records for those ranges (target is a fact table).
I am trying to achieve this in plain SQL query (no stored procedure).
Can you think of a trick ?
Declare #t table(
SnapShotDay date,
OperationalUnitNumber int,
IsOpen bit,
StatusDate date
)
insert into #t
select '1-01-2014', 001 , 1 , '1-01-2014' union all
select '2-01-2014', NULL, NULL, NULL union all
select '3-01-2014', 001 , 0 ,'3-01-2014' union all
select '4-01-2014', NULL,NULL,NULL union all
select '5-01-2014', 001 ,1,'5-01-2014'
;
with CTE as
(
select *,row_number()over( order by (select 0))rn from #t
)
select *,
case when a.isopen is null then (
select IsOpen from cte where rn=a.rn-1
) else a.isopen end
from cte a
ok i got it create one more cte1 then,
,cte1 as
(
select top 1 rn ,IsOpen from cte where IsOpen is not null order by rn desc
)
--select * from Statuses
select *,
case
when a.rn<=(select b.rn from cte1 b) and a.IsOpen is null then
(
select
a1.IsOpen
from
cte a1
where
a1.rn=a.rn-1
)
when a.rn>=(select b.rn from cte1 b) and a.IsOpen is null then
(select IsOpen from cte1)
else
a.isopen
end
from
cte a
Try this. In the main query we're looking for the previous date with not null values. Then just JOIN this table with this LastDate.
WITH T1 AS
(
SELECT *, (SELECT MAX(SnapShotDay)
FROM T
WHERE SnapShotDay<=TMain.SnapShotDay
AND OPERATIONALUNITNUMBER IS NOT NULL)
as LastDate
FROM T as TMain
)
SELECT T1.SnapShotDay,
T.OperationalUnitNumber,
T.IsOpen,
T.StatusDate
FROM T1
JOIN T ON T1.LastDate=T.SnapShotDay
SQLFiddle demo
SELECT
t1.SnapShotDay,
CASE WHEN t1.OperationalUnitNumber IS NOT NUll
THEN t1.OperationalUnitNumber
ELSE (SELECT TOP 1 t2.OperationalUnitNumber FROM YourTable t2 WHERE t2.SnapShotDay < t1.SnapShotDay AND t2.OperationalUnitNumber IS NOT NULL ORDER BY SnapShotDay DESC)
END AS OperationalUnitNumber,
CASE WHEN t1.IsOpen IS NOT NUll
THEN t1.IsOpen
ELSE (SELECT TOP 1 t2.IsOpen FROM YourTable t2 WHERE t2.SnapShotDay < t1.SnapShotDay AND t2.IsOpen IS NOT NULL ORDER BY SnapShotDay DESC)
END AS IsOpen,
CASE WHEN t1.StatusDate IS NOT NUll
THEN t1.StatusDate
ELSE (SELECT TOP 1 t2.StatusDate FROM YourTable t2 WHERE t2.SnapShotDay < t1.SnapShotDay AND t2.StatusDate IS NOT NULL ORDER BY SnapShotDay DESC)
END AS StatusDate
FROM YourTable t1
You asked for 'plain sql', here is a tested attempt using SQL, with comments, that gives the required answer.
I have tested the code using 'sqlite' and 'mysql' on windows xp. It is pure SQL and should work everywhere.
SQL is about 'sets' and combining them and ordering the results.
This problem seems to be about two separate sets:
1) The 'snap shot day' that have readings.
2) the 'snap shot day' that don't have readings.
I have added extra columns so that we can easily see where values came from.
let us deal with the easy set first:
This is the set of 'supplied' readings.
SELECT dss.SnapShotDay theDay,
'supplied' readingExists,
dss.OperationalUnitNumber,
dss.IsOpen,
dss.StatusDate
FROM dailysnapshot dss
WHERE dss.OperationalUnitNumber IS NOT NULL
results:
theDay readingExists OperationalUnitNumber IsOpen StatusDate
2014-01-01 supplied 001 1 2014-01-01
2014-01-03 supplied 001 0 2014-01-03
2014-01-05 supplied 001 1 2014-01-05
Now let us deal with the set of 'days that have missing readings'. We need to get the 'most recent day that has readings that is closest to the day with the missing readings' and assume the same values from the 'most recent day' that is before the 'current' missing day.
It sounds complex but it isn't. It asks:
foreach day without a reading - get me the closest, earlier, date that has readings and i will use those readings.
Here is the query:
SELECT emptyDSS.SnapShotDay,
'missing' readingExists,
maxPrevDSS.OperationalUnitNumber,
maxPrevDSS.IsOpen,
maxPrevDSS.StatusDate
FROM dailysnapshot emptyDSS
INNER JOIN dailysnapshot maxPrevDSS ON maxPrevDSS.SnapShotDay =
(SELECT MAX(dss.SnapShotDay)
FROM dailysnapshot dss
WHERE dss.SnapShotDay < emptyDSS.SnapShotDay
AND dss.OperationalUnitNumber IS NOT NULL)
WHERE emptyDSS.OperationalUnitNumber IS NULL
results:
SnapShotDay readingExists OperationalUnitNumber IsOpen StatusDate
2014-01-02 missing 001 1 2014-01-01
2014-01-04 missing 001 0 2014-01-03
This is not about efficiency! It is about getting the correct 'result set' with the easiest to understand SQL code. I assume the database engine will optimize the query. The query can be 'tweaked' later if required.
We now need to combine the two queries and order the results in the manner we require.
The standard way of combining results from SQL queries is with set operators (union, intersection, minus).
we use 'union' and an 'order by' on the result set.
this gives the final query of:
SELECT dss.SnapShotDay theDay,
'supplied' readingExists,
dss.OperationalUnitNumber,
dss.IsOpen,
dss.StatusDate
FROM dailysnapshot dss
WHERE `OperationalUnitNumber` IS NOT NULL
UNION
SELECT emptyDSS.SnapShotDay theDay,
'missing' readingExists,
maxPrevDSS.OperationalUnitNumber,
maxPrevDSS.IsOpen,
maxPrevDSS.StatusDate
FROM dailysnapshot emptyDSS
INNER JOIN dailysnapshot maxPrevDSS ON maxPrevDSS.SnapShotDay =
(SELECT MAX(dss.SnapShotDay)
FROM dailysnapshot dss
WHERE dss.SnapShotDay < emptyDSS.SnapShotDay
AND dss.OperationalUnitNumber IS NOT NULL)
WHERE emptyDSS.OperationalUnitNumber IS NULL
ORDER BY theDay ASC
result:
theDay readingExists dss.OperationalUnitNumber dss.IsOpen dss.StatusDate
2014-01-01 supplied 001 1 2014-01-01
2014-01-02 missing 001 1 2014-01-01
2014-01-03 supplied 001 0 2014-01-03
2014-01-04 missing 001 0 2014-01-03
2014-01-05 supplied 001 1 2014-01-05
I enjoyed doing this.
It should work with most SQL engines.

SQL Server 2008 how to select top [column value] and random record?

I'm using SQL Server 2008, I want select random row record, and the total number of record is depend on another table's column value, how to do this?
My SQL statement is something like this, but wrong..
select top b.number a.name, a.link_id
from A a
left join B b on b.link_id = a.link_id
order by newid()
Here are my tables and the expected result.
Table A:
name link_id
james 100
albert 100
susan 100
simon 101
tom 101
fion 101
Table B:
link_id number
100 2
101 1
Expected result:
when run 1st time, result may be:
name link_id
james 100
susan 100
fion 101
2nd time result may be:
albert 100
susan 100
simon 101
3rd time could be:
james 100
albert 100
fion 101
Explaination
Refer to table B, link_id: 100, number: 2
meaning that Table A should select out 2 random record for link_id = 100
and need to select 1 random record for link_id=101
You can use the ROW_NUMBER() function:
SELECT A.name, A.link_id
FROM(
SELECT name,link_id, ROW_NUMBER()OVER(PARTITION BY link_id ORDER BY NEWID()) rn
FROM dbo.tblA
) AS A
JOIN dbo.tblB AS B
ON A.link_id = B.link_id
WHERE A.rn <= B.number;
Here is a SqlFiddle to show this in action: http://sqlfiddle.com/#!3/92eac/2
Try this:
SELECT a.*
FROM b
CROSS APPLY
(
SELECT TOP (b.number) a.*
FROM a
WHERE a.link_id = b.link_id
ORDER BY
NEWID()
) a
Also see: SQLFiddle

Resources