I have a table that looks like the following which was created using the following code...
SELECT Orders.ID, Orders.CHECKIN_DT_TM, Orders.CATALOG_TYPE,
Orders.ORDER_STATUS, Orders.ORDERED_DT_TM, Orders.COMPLETED_DT_TM,
Min(DateDiff("n",Orders.ORDERED_DT_TM,Orders.COMPLETED_DT_TM)) AS
Time_to_complete
FROM Orders
GROUP BY Orders.ORDER_ID, Orders.ID,
Orders.CHECKIN_DT_TM, Orders.CATALOG_TYPE, Orders.ORDERED_DT_TM,
Orders.COMPLETED_DT_TM, HAVING (((Orders.CATALOG_TYPE)="radiology");
ID Time_to_complete ... .....
1 5
1 7
1 8
2 23
2 6
3 7
4 16
4 14
I'd like to add to this code which would select the smallest Time_to_complete value per subject ID. Leaving the desired table:
ID Time_to_complete ... .....
1 5
2 6
3 7
4 14
I'm using Access and prefer to continue using Access to finish this code but I do have the option to use SQL Server if this is not possible in Access. Thanks!
I suspect you need correlated subquery :
SELECT O.*, DateDiff("n", O.ORDERED_DT_TM, O.COMPLETED_DT_TM) AS Time_to_complete
FROM Orders O
WHERE DateDiff("n", O.ORDERED_DT_TM, O.COMPLETED_DT_TM) = (SELECT Min(DateDiff("n", O1.ORDERED_DT_TM, O1.COMPLETED_DT_TM))
FROM Orders O1
WHERE O1.ORDER_ID = O.ORDER_ID AND . . .
);
EDIT : If you want unique records then you can do instead :
SELECT O.*, DateDiff("n", O.ORDERED_DT_TM, O.COMPLETED_DT_TM) AS Time_to_complete
FROM Orders O
WHERE o.pk = (SELECT TOP (1) o1.pk
FROM Orders O1
WHERE O1.ORDER_ID = O.ORDER_ID AND . . .
ORDER BY DateDiff("n", O.ORDERED_DT_TM, O.COMPLETED_DT_TM) ASC
);
pk is your identity column that specifies unique entry in Orders table, so you can change it accordingly.
Have a look at this:
DECLARE #myTable AS TABLE (ID INT, Time_to_complete INT);
INSERT INTO #myTable
VALUES (1, 5)
, (1, 7)
, (1, 8)
, (2, 23)
, (2, 6)
, (3, 7)
, (4, 16)
, (4, 14);
WITH cte AS
(SELECT *
, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Time_to_complete) AS RN
FROM #myTable)
SELECT cte.ID
, cte.Time_to_complete
FROM cte
WHERE RN = 1;
Results :
ID Time_to_complete
----------- ----------------
1 5
2 6
3 7
4 14
It uses row numbers over groups, then selects the first row for each group. You should be able to adjust your code to use this technique. If in doubt wrap your entire query in a cte first then apply the technique here.
It's worth becoming familiar with this process as it gets used in a lot of places - especially around de-duping data.
Try This
DECLARE #myTable AS TABLE (ID INT, Time_to_complete INT);
INSERT INTO #myTable
VALUES (1, 5)
, (1, 7)
, (1, 8)
, (2, 23)
, (2, 6)
, (3, 7)
, (4, 16)
, (4, 14);
SELECT O.ID, O.Time_to_complete
FROM #myTable O
WHERE o.Time_to_complete = (Select min(m.Time_to_complete) FROM #myTable m
Where o.id=m.ID
);
Result :
ID Time_to_complete
1 5
2 6
3 7
4 14
Related
I have a table with a list of Jobs that have children, grandchildren, etc. There is no limit on the level of hierarchy it goes down. The table has ID, Name, and ParentID. So for example, a Job table could look like:
ID Name Parent ID
1 Education null
2 IT null
3 Teacher 1
4 MS Teacher 3
5 7th Grade 4
6 Sys Admin 2
7 HS Teacher 3
8 12th Grade 7
9 IT Support 6
10 Developer 2
There is also a UserToJob table that is just the JobID and UserID. A person could be listed in more than one Job.
I'm looking for the most efficient way to get all people with a specified job and all decedents, so if I want to query for Education then it returns Education, Teacher, MS Teacher, 7th Grade, HS Teacher, and 12th Grade.
Right now my best attempt looks like
with
Closure AS (
select j.ID as AncestorID, j.ID as DescendantID, 0 as Depth from Jobs j
UNION ALL
select CTE.AncestorID, j.ID, CTE.Depth + 1 from Jobs j
inner join Closure CTE on j.ParentID = CTE.DescendantID
),
Job AS ( select j.UserID as ID from UserToJob j
where j.JobID in (select DescendantID from Closure where AncestorID in (1))
)
I want it to be able to work querying more than one job at a time, for example if I wanted all Education and Sys Admins then I'd change AncestorID in (1) to AncestorID in (1, 6) in the final line of my attempt.
You have your where clause in the wrong place. You want to limit the root of your recursive cte to only return the first level rows you are concerned with.
This should point you in the right direction.
declare #Jobs table
(
ID int
, Name varchar(50)
, ParentID int
)
insert #Jobs values
(1, 'Education', null)
, (2, 'IT', null)
, (3, 'Teacher', 1)
, (4, 'MS Teacher', 3)
, (5, '7th Grade', 4)
, (6, 'Sys Admin', 2)
, (7, 'HS Teacher', 3)
, (8, '12th Grade', 7)
, (9, 'IT Support', 6)
, (10, 'Developer', 2)
select *
from #Jobs;
with Closure AS
(
select j.ID as AncestorID
, j.ID as DescendantID
, 0 as Depth
from #Jobs j
where j.ID in (1, 6)
UNION ALL
select CTE.AncestorID
, j.ID
, CTE.Depth + 1
from #Jobs j
inner join Closure CTE on j.ParentID = CTE.DescendantID
)
select *
from Closure
I have a query
select *
from qtable
where qid in (15,555,2,3,4,5,36,27,18,9)
here qid is the primary key.
I get the following results sorted out by qid as it is the primary key but my requirement is to get the results as mentioned in the in (15, 555, 2, 3, 4, 5, 36, 27, 18, 9) statement.
Is there a way to unsort the records?
select qtable.*
from qtable join (values (15),(55),(2),(3),(4))a(id)
on a.Id = qtable.qID
SQL Server and other ANSI SQL compliant database require an ORDER BY clause to return data in a particular order. One approach is to use a row constructor that includes the desired sequence:
SELECT qtable.*
FROM qtable
JOIN (VALUES
(1, 15)
,(2, 555)
,(3, 2)
,(4, 3)
,(5, 4)
,(6, 5)
,(7, 36)
,(8, 27)
,(9, 18)
,(10, 9)
) AS list(seq, value) ON qtable.quid = list.qid
ORDER BY list.seq;
A similar technique can be used for variable list of values passed as a table-valued parameter or table variable:
DECLARE #list TABLE (
seq int
, qid int);
INSERT INTO #list VALUES
(1, 15)
,(2, 555)
,(3, 2)
,(4, 3)
,(5, 4)
,(6, 5)
,(7, 36)
,(8, 27)
,(9, 18)
,(10, 9);
SELECT qtable.*
FROM qtable
JOIN #list AS list ON qtable.quid = list.qid
ORDER BY list.seq;
You can do it like this:
select qid,case when qid = 15 then 1
when qid = 555 then 2
when qid = 2 then 3
when qid = 3 then 4
when qid = 4 then 5
when qid = 5 then 6
when qid = 36 then 7
when qid = 27 then 8
when qid = 18 then 9
when qid = 9 then 10
end as qidflag
from qtable where qid in (15,555,2,3,4,5,36,27,18,9)
order by qidflag
Here's an option where you can pass the desired ID/Sequence as a delimited string.
The Sequence will be maintained.
Example
Declare #List varchar(max)='15,555,2,3,4,5,36,27,18,9'
Select A.*
From qtable A
Join (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = v.value('(./text())[1]', 'int')
From (values (convert(xml,'<x>' + replace(#List,',','</x><x>')+'</x>'))) x(n)
Cross Apply n.nodes('x') node(v)
) B on A.quid = B.RetVal
Order By B.RetSeq
If it Helps - the sub-query Returns
RetSeq RetVal
1 15
2 555
3 2
4 3
5 4
6 5
7 36
8 27
9 18
10 9
I would use something similar to the following:
DECLARE #qidList TABLE (sort INT NOT NULL IDENTITY(1,1), qid INT);
INSERT #qidList VALUES
(15),
(555),
(2),
(3),
(4),
(5),
(36),
(27),
(18),
(9);
select qtable.*
from qtable
join #qidList list on list.qid=qtable.qid
order by list.sort;
This is not a homework question.
I'm trying to take the count of t-shirts in an order and see which price range the shirts fall into, depending on how many have been ordered.
My initial thought (I am brand new at this) was to ask another table if count > 1st price range's maximum, and if so, keep looking until it's not.
printing_range_max printing_price_by_range
15 4
24 3
33 2
So for example here, if the order count is 30 shirts they would be $2 each.
When I'm looking into how to do that, it looks like most people are using BETWEEN or IF and hard-coding the ranges instead of looking in another table. I imagine in a business setting it's best to be able to leave the range in its own table so it can be changed more easily. Is there a good/built-in way to do this or should I just write it in with a BETWEEN command or IF statements?
EDIT:
SQL Server 2014
Let's say we have this table:
DECLARE #priceRanges TABLE(printing_range_max tinyint, printing_price_by_range tinyint);
INSERT #priceRanges VALUES (15, 4), (24, 3), (33, 2);
You can create a table with ranges that represent the correct price. Below is how you would do this in pre-2012 and post-2012 systems:
DECLARE #priceRanges TABLE(printing_range_max tinyint, printing_price_by_range tinyint);
INSERT #priceRanges VALUES (15, 4), (24, 3), (33, 2);
-- post-2012 using LAG
WITH pricerange AS
(
SELECT
printing_range_min = LAG(printing_range_max, 1, 0) OVER (ORDER BY printing_range_max),
printing_range_max,
printing_price_by_range
FROM #priceRanges
)
SELECT * FROM pricerange;
-- pre-2012 using ROW_NUMBER and a self-join
WITH prices AS
(
SELECT
rn = ROW_NUMBER() OVER (ORDER BY printing_range_max),
printing_range_max,
printing_price_by_range
FROM #priceRanges
),
pricerange As
(
SELECT
printing_range_min = ISNULL(p2.printing_range_max, 0),
printing_range_max = p1.printing_range_max,
p1.printing_price_by_range
FROM prices p1
LEFT JOIN prices p2 ON p1.rn = p2.rn+1
)
SELECT * FROM pricerange;
Both queries return:
printing_range_min printing_range_max printing_price_by_range
------------------ ------------------ -----------------------
0 15 4
15 24 3
24 33 2
Now that you have that you can use BETWEEN for your join. Here's the full solution:
-- Sample data
DECLARE #priceRanges TABLE
(
printing_range_max tinyint,
printing_price_by_range tinyint
-- if you're on 2014+
,INDEX ix_xxx NONCLUSTERED(printing_range_max, printing_price_by_range)
-- note: second column should be an INCLUDE but not supported in table variables
);
DECLARE #orders TABLE
(
orderid int identity,
ordercount int
-- if you're on 2014+
,INDEX ix_xxy NONCLUSTERED(orderid, ordercount)
-- note: second column should be an INCLUDE but not supported in table variables
);
INSERT #priceRanges VALUES (15, 4), (24, 3), (33, 2);
INSERT #orders(ordercount) VALUES (10), (20), (25), (30);
-- Solution:
WITH pricerange AS
(
SELECT
printing_range_min = LAG(printing_range_max, 1, 0) OVER (ORDER BY printing_range_max),
printing_range_max,
printing_price_by_range
FROM #priceRanges
)
SELECT
o.orderid,
o.ordercount,
--p.printing_range_min,
--p.printing_range_max
p.printing_price_by_range
FROM pricerange p
JOIN #orders o ON o.ordercount BETWEEN printing_range_min AND printing_range_max
Results:
orderid ordercount printing_price_by_range
----------- ----------- -----------------------
1 10 4
2 20 3
3 25 2
4 30 2
Now that we have that we can
I have a data set within Microsoft SQL that looks like so
ID Value1 Value2
1 8 4
1 4 2
1 9 3
1 3 1
2 4 9
2 5 7
2 6 4
2 7 5
2 1 1
I am trying to pull only the data from the corresponding row containing a max value in column 1 grouped by the ID number. The result should be as follows
ID Value1 Value2
1 9 3
2 7 5
The following is what I have tried, but have been unsuccessful. It works if Value2 is removed.
USE [Database]
SELECT [ID],
MAX([Value1]) as Value1,
[Value2]
FROM [dbo].[Datatable]
GROUP BY [ID]
The ROW_NUMBER() window function can be used to partition the table by ID and rank the data within (by Value1 in descending order, in this case).
DECLARE #DataTable TABLE (ID INT, Value1 INT, Value2 INT);
INSERT #DataTable (ID, Value1, Value2)
VALUES (1, 8, 4)
, (1, 4, 2)
, (1, 9, 3)
, (1, 3, 1)
, (2, 4, 9)
, (2, 5, 7)
, (2, 6, 4)
, (2, 7, 5)
, (2, 1, 1);
SELECT ID, Value1, Value2
FROM (
SELECT ID, Value1, Value2, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Value1 DESC) RN
FROM #DataTable) T
WHERE RN = 1;
Alternatively, if there are possible matches on the maximum value of Value1, use RANK() (or DENSE_RANK())
Did you try searching for the Max value of value2 ? Like this
USE [Database]
SELECT [ID],
MAX([Value1]) as Value1,
MAX([Value2]) as Value2
FROM [dbo].[Datatable]
GROUP BY [ID]
I have a problem with a query.
This is the data (order by Timestamp):
Data
ID Value Timestamp
1 0 2001-1-1
2 0 2002-1-1
3 1 2003-1-1
4 1 2004-1-1
5 0 2005-1-1
6 2 2006-1-1
7 2 2007-1-1
8 2 2008-1-1
I need to extract distinct values and the first occurance of the date. The exception here is that I need to group them only if not interrupted with a new value in that timeframe.
So the data I need is:
ID Value Timestamp
1 0 2001-1-1
3 1 2003-1-1
5 0 2005-1-1
6 2 2006-1-1
I've made this work by a complicated query, but am sure there is an easier way to do it, just cant think of it. Could anyone help?
This is what I started with - probably could work with that. This is a query that should locate when a value is changed.
> SELECT * FROM Data d1 join Data d2 ON d1.Timestamp < d2.Timestamp and
> d1.Value <> d2.Value
It probably could be done with a good use of row_number clause but cant manage it.
Sample data:
declare #T table (ID int, Value int, Timestamp date)
insert into #T(ID, Value, Timestamp) values
(1, 0, '20010101'),
(2, 0, '20020101'),
(3, 1, '20030101'),
(4, 1, '20040101'),
(5, 0, '20050101'),
(6, 2, '20060101'),
(7, 2, '20070101'),
(8, 2, '20080101')
Query:
;With OrderedValues as (
select *,ROW_NUMBER() OVER (ORDER By TimeStamp) as rn --TODO - specific columns better than *
from #T
), Firsts as (
select
ov1.* --TODO - specific columns better than *
from
OrderedValues ov1
left join
OrderedValues ov2
on
ov1.Value = ov2.Value and
ov1.rn = ov2.rn + 1
where
ov2.ID is null
)
select * --TODO - specific columns better than *
from Firsts
I didn't rely on the ID values being sequential and without gaps. If that's the situation, you can omit OrderedValues (using the table and ID in place of OrderedValues and rn). The second query simply finds rows where there isn't an immediate preceding row with the same Value.
Result:
ID Value Timestamp rn
----------- ----------- ---------- --------------------
1 0 2001-01-01 1
3 1 2003-01-01 3
5 0 2005-01-01 5
6 2 2006-01-01 6
You can order by rn if you need the results in this specific order.