I have a table containing multiple columns, and I want to order by three of the columns within the table. I want to order by lot, then unit, and lastly number. Lot and Unit can contain nulls (need them listed at the bottom) whereas Number column does not contain Null. I've tried a bunch of case statements but its not giving the desired result. - Order by ... Nulls last is not an option.
Lot
Unit
Number
Null
Null
500
Null
Null
425
Null
Null
424
Null
7
419
Null
9
450
25
Null
475
22
Null
486
22
Null
485
19
7
465
19
9
432
Desired result:
Lot
Unit
Number
19
7
465
19
9
432
22
Null
485
22
Null
486
25
Null
475
Null
7
419
Null
9
450
Null
Null
424
Null
Null
425
Null
Null
500
you can do order by checking the nulls:
SELECT *
FROM yourtable
ORDER BY CASE WHEN lot IS NULL THEN 1 ELSE 0 END
, lot
, CASE WHEN unit IS NULL THEN 1 ELSE 0 END
, unit
, number
You have tagged your question SQL, but not mentioned the database. Standard SQL has a very convenient way to do what you want:
order by Lot nulls last, Unit nulls last, Number
Not all databases support nulls last and nulls first, but this is the standard SQL solution.
Related
Input table:
STAGE_NO
STAGE_ENTERED_DATE
0
2015-12-01 14:16:47
1
null
2
null
3
null
4
null
5
null
6
2017-02-12 0:00:00
7
2017-12-12 0:00:00
I want a new column that will give me the next stage_no where "stage_entered_date" is not-null.
This is the result that I am expecting:
STAGE_NO
STAGE_ENTERED_DATE
Next_Stage
0
2015-12-01 14:16:47
6
1
null
6
2
null
6
3
null
6
4
null
6
5
null
6
6
2017-02-12 0:00:00
7
7
2017-12-12 0:00:00
null
Disclaimer: Next_Stage column means next_stage where date is not null.
You can do this using the lead window function, ignoring nulls, and redefining nulls as the null in a different column using the IFF function:
with T1 as
(
select
COLUMN1::int as "STAGE_NO",
COLUMN2::timestamp as "STAGE_ENTERED_DATE"
from (values
('0','2015-12-01 14:16:47'),
('1',null),
('2',null),
('3',null),
('4',null),
('5',null),
('6','2017-02-12 0:00:00'),
('7','2017-12-12 0:00:00')
)
)
select STAGE_NO
,STAGE_ENTERED_DATE
,lead(iff(STAGE_ENTERED_DATE is not null, STAGE_NO, null))
ignore nulls over (partition by null order by STAGE_NO) as NEXT_STAGE
from T1
;
STAGE_NO
STAGE_ENTERED_DATE
NEXT_STAGE
0
2015-12-01 14:16:47.000000000
6
1
null
6
2
null
6
3
null
6
4
null
6
5
null
6
6
2017-02-12 00:00:00.000000000
7
7
2017-12-12 00:00:00.000000000
null
This will perform the stage calculation across the entire table. You probably have something like a customer, company, or some other "thing" that goes through these stages. You can specify what that is using a partition by clause in the window function. It's currently set to null, but you can simply change it to the column that defines the sets of rows for the phases.
I've never posted on here before but, I am really stumped on this and looking for any assistance I get! I am not the best SQL code writer and I do not understand every concept but I am quick learner. So, I am not sure this is best way to accomplish my goal and if there is a more efficient way to complete this, I would be open to learning. I appreciate any help that can be provided.
Task:
I am attempting to write a SQL code that will help me place a number under the "Grab" column that allows me to exclude other rows out that are not needed.
Issue:
Pricing has a timeframe when it is applicable. The [PriceBookTable] captures the time frame range for each price book that is listed. However, as time goes on, some price books become outdated and do not need to be reviewed.
Based on today's date, I am trying to identify the previous version price book as well as the next version (if there is one).
Table Used: [PriceBookTable]
ID
Description
CategoryID
ParentID
StartDate
EndDate
412
56 MSRP
56
NULL
NULL
NULL
413
3 MSRP
3
NULL
NULL
NULL
414
61 MSRP
61
NULL
NULL
NULL
415
63 MSRP
63
NULL
NULL
NULL
419
58 MSRP
58
NULL
NULL
NULL
420
62 MSRP
62
NULL
NULL
NULL
430
67 MSRP
67
NULL
NULL
NULL
431
68 MSRP
68
NULL
NULL
NULL
505
2020 Version 1
56
412
2020-08-31
2020-12-31
537
2021 Version 1
56
412
2021-01-01
2021-03-31
586
2021 Version 2
56
412
2021-04-01
2021-04-13
622
2021 Version 3
56
412
2021-04-14
2021-07-31
688
2021 Version 4
56
412
2021-08-01
2021-12-31
Current Code:
USE [Database]
DECLARE #PriceBookID AS VARCHAR(10)
SET #PriceBookID = '412' --Parent Price Book ID
SELECT A.*,
[Grab] = CASE WHEN A.ParentID IS NULL AND A.StartDate IS NULL AND A.EndDate IS NULL THEN 1 -- Always needs to be #1
WHEN CAST(GETDATE() AS DATE) BETWEEN A.StartDate AND A.EndDate THEN 3 --Currently Active Price Book based on Today's Date
ELSE NULL END
FROM( SELECT ID,
ParentID,
[PriceBookDescription] = Description,
StartDate,
EndDate,
[ActivePriceBook] = CASE WHEN CAST(GETDATE() AS DATE) BETWEEN StartDate AND EndDate THEN 'Active' ELSE NULL END,
[PBOrder] = ROW_NUMBER() OVER (ORDER BY ID ASC)
FROM [PriceBookTable]
WHERE 1=1 AND ID IN (#PriceBookID) OR ParentID IN (#PriceBookID)) A
Current Output:
ID
ParentID
PriceBookDescription
StartDate
EndDate
ActivePriceBook
PBOrder
Grab
412
Null
MSRP
NULL
NULL
NULL
1
1
505
412
2020 Version 1
2020-08-31
2020-12-31
NULL
2
NULL
537
412
2021 Version 1
2021-01-01
2021-03-31
NULL
3
NULL
586
412
2021 Version 2
2021-04-01
2021-04-13
NULL
4
NULL
622
412
2021 Version 3
2021-04-14
2021-07-31
Active
5
3
688
412
2021 Version 4
2021-08-01
2021-12-31
NULL
6
NULL
Notes:
I originally was hoping that the "PBOrder" column would be useful for me but, as time goes on the list becomes bigger as more price books are created and, for example, row #4 [ID 586] will not always be relevant.
I would have just placed a "WHERE ID IN ('412','586','622','688')" statement but the ID's change based on different categories (not shown). So, I am stuck to the date range.
Desired Output:
ID
ParentID
PriceBookDescription
StartDate
EndDate
ActivePriceBook
PBOrder
Grab
412
Null
MSRP
NULL
NULL
NULL
1
1
586
412
2021 Version 2
2021-04-01
2021-04-13
NULL
4
2
622
412
2021 Version 3
2021-04-14
2021-07-31
Active
5
3
688
412
2021 Version 4
2021-08-01
2021-12-31
NULL
6
4
I hope this makes sense and please let me know if you have any questions regarding this.
Thank you again for any help!
Took me awhile to understand what you wanted, but after figuring it out I was able to address what you need. Basically, you want:
To identify a single active record within a category based on the current date.
Then get the adjacent inactive records, with respect to time, that share a parent record.
Then get the record for the parent category and include it in the result set.
The 'pbOrder' and 'grab' columns seem to be throughputs to achieve this goal. You don't need them in the output.
If this is all correct, then you can delegate your identification of an active record to a cross apply calculation, and then use lead and lag in addition to the raw result to identify the active record as well as the adjacent ones in time.
declare #PriceBookID int = 412; -- why varchar, I would use int
with rowsToGrab as (
select pbt.*,
ap.activePriceBook,
grab =
case
when pbt.ParentID is null then 1
when lead(ap.ActivePriceBook) over(order by pbt.startDate) is not null then 1
when lag(ap.ActivePriceBook) over(order by pbt.startDate) is not null then 1
when ap.ActivePriceBook is not null then 1
end
from #PriceBookTable pbt
cross apply (select ActivePriceBook =
case
when cast(getdate() as date) between startdate and enddate then 'Active'
end
) ap
where #PriceBookID in (ID, ParentID)
)
select id, ParentID, description as PriceBookDescription, StartDate, EndDate, ActivePriceBook
from rowsToGrab
where grab is not null
order by id, StartDate
This produces:
id
ParentID
PriceBookDescription
StartDate
EndDate
ActivePriceBook
412
56 MSRP
586
412
2021 Version 2
2021-04-01
2021-04-13
622
412
2021 Version 3
2021-04-14
2021-07-31
Active
688
412
2021 Version 4
2021-08-01
2021-12-31
I have a scenario in which I have a table that is combination of many-to-many relationship, I want to make sure that relationship of association exists only once , no repeating records do exists in it.
I am pasting data over here, I need to make sure that there is not repeating , that violates "gerund" property.
ItemID ProductID ProductAssociationGroupID
3064 10084 11
3065 10705 11
3066 11766 68
3067 11766 75
3068 11772 106
3069 11778 11
3070 11779 98
3071 11779 93
3072 11793 93
3073 12073 20
3074 12178 12
3075 12561 12
3076 12561 17
3077 12561 82
3078 12561 81
3079 12561 77
3080 12561 76
3081 12573 37
How may I query to find out the exact relationship exists only once in the above data?
I am still awaiting some clarification on the columns, but I believe using the HAVING clause will give you the results you want.
Select ProductID, ProductAssociationGroupID
from [Database]
Group by ProductAssociationGroupID, ProductID
Having (Count(ProductAssociationGroupID) = 1)
If you want to find duplicates, then I would just change the HAVING clause to greater than.
The approach I generally use is to use SQL to GROUP BY the columns that you want to ensure are unique, and then specify that the query should only return results HAVING a COUNT(*) > 1 to find any where there are multiple values.
E.g.
SELECT
ProductID
, ProductAssociationGroupID
, COUNT(*) AS CountRows
FROM
YourTable
GROUP BY
ProductID
, ProductAssociationGroupID
HAVING
COUNT(*) > 1
This should only return rows where there are duplicates.
I have a query in Access 2010 (have also tried on 2013, same result) that is working but not perfectly for all records. I'm wondering if anyone knows what is causing the error.
Here is the query (adapted from http://allenbrowne.com/subquery-01.html#AnotherRecord):
SELECT t_test_table.individ, t_test_table.test_date, t_test_table.score1, (SELECT top 1 Dupe.score1
FROM t_test_table AS Dupe
WHERE Dupe.individ = t_test_table.individ
AND Dupe.test_date < t_test_table.test_date
ORDER BY Dupe.primary DESC, Dupe.individ
) AS PriorValue, [score1]-[priorvalue] AS scorechange
FROM t_test_table;
The way the data is set up, an individual has multiple records in the file (designated by individ) representing different dates a test was taken. A date AND individ combination are unique - you can only take a test once. [primary] refers to primary key column. I just made it because the individ field is not a primary key since multiples are possible (I'm not including it here due to space)
The goal of the above code was to create the following:
individ test_date score1 PriorValue scorechange
1 3/1/2013 40
1 6/4/2013 51 40 11
1 7/25/2013 55 51 4
1 12/13/2013 59 55 4
5 8/29/2009 39
5 12/9/2009 47 39 8
5 6/1/2010 58 47 11
5 8/28/2010 42 58 -16
5 12/15/2010 51 42 9
Here is what I actually got. You can see that for individ 1, it winds up taking the first score rather than the previous score for each subsequent record. For individ 5, it kind of works, but the final priorvalue should be 42 and not 58.
individ test_date score1 PriorValue scorechange
1 3/1/2012 40
1 6/4/2012 51 40 11
1 7/25/2012 55 40 15
1 12/13/2012 59 40 19
5 8/29/2005 39
5 12/9/2005 47 39 8
5 6/1/2006 58 47 11
5 8/28/2006 42 58 -16
5 12/15/2006 51 58 -7
Does anyone have any ideas about what went wrong here? In other records, it works perfectly, but I can't determine what is causing some records to fail to take the previous value.Any help is appreciated, and let me know if you require additional information.
To get the most recent test for a given individ, you'll need to include a sort by date. In your inner query, replace
ORDER BY Dupe.primary DESC, Dupe.individ
with
ORDER BY Dupe.test_date DESC
It's hard to say exactly what effect sorting by primary has, since you haven't told us how you're generating the values of primary. If the combination of individ and test_date is guaranteed to be unique, you might want to consider making the two of them into your primary key instead of creating a new thing. The Dupe.individ in the ORDER BY line has no effect, since your WHERE clause already limited the results of the inner query to one individ.
I have two tables:
1. #Forecast_Premiums
Syndicate_Key Durg_Key Currency_Key Year_Of_Account Forecast_Premium CUML_EPI_Amount
NULL NULL NULL UNKNOWN 0 6
3 54 46 2000 109105 0
3 54 46 2001 128645 128646
5 47 80 2002 117829 6333
6 47 80 2002 125471 NULL
6 60 80 2003 82371 82371
10 98 215 2006 2093825 77888
10 98 215 2007 11111938 4523645
2.#Forecast_Claims
Syndicate_Key Durg_Key Currency_Key Year_Of_Account Contract_Ref Forecast_Claims Ultimate_Profit_Comission
NULL NULL NULL UNKNOWN UNKNOWN 0 -45
5 47 80 2002 AB00ZZ021M12 -9991203 NULL
5 47 80 2002 AB00ZZ021M13 -4522 -74412
9 60 215 2006 AC04ZZ021M13 -2340299 -895562
10 98 46 2007 FAC0ZZ021M55 -2564123 -851298
The task:
Using #Forecast_Premiums and #Forecast_Claims tables write a query to find
total amount of Pure Premium ,Cumulative EPI Amount, Forecast_Claims and Ultimate_Profit_Comissionreceived for each combination of Syndicate_Key, Durg_Key , Currency_key and Year_of_Account.
Note: In case the Key is NULL set it as 'UNKNOWN' , In Case the Amount is NULL set it as 0.
My solution:
SELECT
ISNULL(CAST(FP.Syndicate_key AS VARCHAR(20)), 'UNKNOWN') AS 'Syndicate_key',
ISNULL(CAST(FP.Durg_Key AS VARCHAR(20)), 'UNKNOWN') AS 'Durg_Key',
ISNULL(CAST(FP.Currency_Key AS VARCHAR(20)), 'UNKNOWN') AS 'Currency_Key',
fp.Year_Of_Account,
SUM(ISNULL(FP.Forecast_Premium,0)) AS 'Pure_Premium',
SUM(ISNULL(FP.CUML_EPI_Amount,0)) AS 'Cuml_Amount',
SUM(ISNULL(dc.Forecast_Claims,0)) AS 'Total_Claims',
SUM(ISNULL(dc.Ultimate_Profit_Comission,0)) AS 'Total_Comission'
FROM #FORECAST_PREMIUMS fp
left join #FORECAST_Claims dc
ON
(FP.Year_Of_Account = dc.Year_Of_Account AND
FP.Syndicate_Key = dc.Syndicate_Key AND
FP.Currency_Key = dc.Currency_Key AND
FP.Year_Of_Account = dc.Year_Of_Account)
GROUP BY fp.Syndicate_Key, fp.Durg_Key,fp.Currency_Key,fp.Year_Of_Account
Issue:
It returns the Forecast_Claims SUM and Ultimate_Profit_Comission SUM only for one combination of keys and year: 5 47 80 2002.
Moreover it returns 8 rows when it should had return 10.
Eight result records is correct, for there are eight distinct combinations of Syndicate_Key, Durg_Key , Currency_key and Year_of_Account in FORECAST_PREMIUMS.
As to the Forecast_Claims SUM: This is also correct; 5 47 80 2002 is the only combination that has a match in Forecast_Claims.
Only: Are you supposed to match both NULL records? You don't do this, as NULL = NULL is never true (only NULL is NULL is true). You would have to do something like
(
(FP.Year_Of_Account = dc.Year_Of_Account)
OR
(FP.Year_Of_Account is null AND dc.Year_Of_Account is null
) AND ...
to get these records match. Or:
ISNULL(FP.Year_Of_Account, -1) = ISNULL(dc.Year_Of_Account, -1) AND ...