How to get minimum value with added string in SQL Server? - sql-server

I have a table with 3 fields: Item Number, Promised Date and Promised Ship Date.
I wanted to get pull the columns Item Number with the earliest promised date so my query is
SELECT
ITEMNMBR,
MIN(PRMDATE) AS [Earliest Promised Date]
FROM
Table1
GROUP BY
ITEMNMBR
This works fine but I wanted to add a string with that [Earliest Promised Date]: Confirmed or Unconfirmed. It is determined by Promised Ship Date. If the promised ship date is equal to the promised date, it is unconfirmed and if they are unequal then it is confirmed.
I have tried but does not work:
SELECT
ITEMNMBR,
MIN(CASE
WHEN PRMDATE = PRMSHPDTE
THEN CONVERT(varchar, PRMDATE, 101) + ' - Unconfirmed'
WHEN PRMDATE <> PRMSHPDTE
THEN CONVERT(varchar, PRMDATE, 101) + ' - Confirmed'
ELSE 'Error'
END) AS [Promised Date]
FROM
Table1
GROUP BY
ITEMNMBR
Can you point out what I have done wrong in the second query?

I believe the problem is rooted in the use of format 101 and alphabetical sorting. You might need to expound on the "(un)confirmed" logic but something like this should get you in the right direction.
SELECT
ITEMNMBR,
CONVERT(VARCHAR(12), MIN(PRMDATE), 101) + ' - ' +
MAX(CASE
WHEN PRMDATE = PRMSHPDTE THEN 'Unconfirmed'
WHEN PRMDATE <> PRMSHPDTE THEN 'Confirmed'
ELSE 'XX Error'
END) AS [Promised Date]
FROM
Table1
GROUP BY
ITEMNMBR;

Related

PIVOT table is converting all columns to DATE

Why does my view get created with FIRM_PANEL and FIRM_DATE as DATEs and not an NVARCHAR and a DATE as I expect?
SELECT DISTINCT piv3.CASE_NUMBER, FIRM_PANEL, FIRM_DATE
FROM
(
SELECT DISTINCT piv2.CASE_NUMBER, piv2.DETAIL_CODE,
CASE piv2.DATA_FLAGS
WHEN 'TEXT_FLAG' THEN CONVERT(NVARCHAR, TEXT_VALUE)
WHEN 'DATE_FLAG' THEN CONVERT(DATE, DATE_VALUE)
END AS 'VALUE'
FROM
(
SELECT DISTINCT CASE_NUMBER, DETAIL_CODE, piv.DATA_FLAGS, TEXT_VALUE, CONVERT(DATE, DATE_VALUE) AS DATE_VALUE
FROM table
UNPIVOT
(
YES_OR_NO
FOR DATA_FLAGS IN (DATE_FLAG, TEXT_FLAG)
) piv
) piv2
) as sourcetable
PIVOT
(
MAX(VALUE)
FOR DETAIL_CODE IN (FIRM_PANEL,
FIRM_DATE )
) AS piv3
Let me explain each nested pivot/select. The first SELECT gives you an idea how the data is store raw. I needed a way to show 1 CASE_NUMBER and DETAIL_CODEs as unique columns.
SELECT DISTINCT CASE_NUMBER, DETAIL_CODE, piv.DATA_FLAGS, TEXT_VALUE,
CONVERT(DATE, DATE_VALUE) AS DATE_VALUE
FROM table
I then needed to take DATE_VALUE and TEXT_VALUE and merge into one column called VALUE, hence my UNPIVOT.
SELECT DISTINCT piv2.CASE_NUMBER, piv2.DETAIL_CODE,
CASE piv2.DATA_FLAGS
WHEN 'TEXT_FLAG' THEN CONVERT(NVARCHAR, TEXT_VALUE)
WHEN 'DATE_FLAG' THEN CONVERT(NVARCHAR, DATE_VALUE)
END AS 'VALUE'
FROM
(
SELECT DISTINCT CASE_NUMBER, DETAIL_CODE, piv.DATA_FLAGS, TEXT_VALUE, CONVERT(DATE, DATE_VALUE) AS DATE_VALUE
FROM table
UNPIVOT
(
YES_OR_NO
FOR DATA_FLAGS IN (DATE_FLAG, TEXT_FLAG)
) piv
) piv2
Then my final SQL (top) finally PIVOTs again and removes nulls and interprets if the column was a text field then use NVARCHAR and if date use DATE.
However the only way I can SELECT from the view without errors is making both NVARCHAR or else I'll receive "Conversion failed when converting date and/or time from character string" due to both columns being a DATE field when they shouldn't be. However I do not understand why the view is created with the two DATE columns. My current workaround is setting as string and just doing the conversion when selecting from the view.
Sorry for the wordiness.
I think an-d was on the right path.. You should definitely try case aggregates.
SELECT CASE_NUMBER,
MAX( CASE WHEN DETAIL_CODE = 'FIRM_PANEL' THEN TEXT_VALUE END) AS FIRM_PANEL,
MAX( CASE WHEN DETAIL_CODE = 'FIRM_DATE' THEN CONVERT(DATE, DATE_VALUE) END) AS FIRM_DATE
FROM table
GROUP BY CASE_NUMBER
You can add more where clauses to the case expression if you need to, but the above may work.
I don't quite understand the reasons for your use of pivots, but the problem seems to be that you're trying to cast the same column VALUE as both DATE and NVARCHAR here -
...
CASE piv2.DATA_FLAGS
WHEN 'TEXT_FLAG' THEN CONVERT(NVARCHAR, TEXT_VALUE)
WHEN 'DATE_FLAG' THEN CONVERT(DATE, DATE_VALUE)
END AS 'VALUE'
...
You can't control when SQL Server performs the CONVERT operation and it might do it before checking the DATA_FLAGS. Your query should work if you try to move the CONVERT outside to the first select -
SELECT DISTINCT piv3.CASE_NUMBER, CONVERT(NVARCHAR, FIRM_PANEL), CONVERT(DATE, FIRM_DATE)
FROM
(
SELECT DISTINCT piv2.CASE_NUMBER, piv2.DETAIL_CODE,
CASE piv2.DATA_FLAGS
WHEN 'TEXT_FLAG' THEN TEXT_VALUE
WHEN 'DATE_FLAG' THEN DATE_VALUE
END AS 'VALUE'
...
If you're open to not using pivots, here's a simple alternative to get the same result -
SELECT CASE_NUMBER
, MAX(CASE WHEN DATA_FLAGS = 'TEXT_FLAG' THEN TEXT_VALUE END) FIRM_DATE
, MAX(CASE WHEN DATA_FLAGS = 'DATE_FLAG' THEN CONVERT(DATE, DATE_VALUE) END) FIRM_PANEL
FROM #in
GROUP BY CASE_NUMBER
This gives the same result as you're looking for with the columns in their expected data types. Note that it assumes you will have only one record with TEXT_FLAG and DATE_FLAG each for every CASE_NUMBER.

Calculate same day start/end dates as 0 days if another occurrence already exists

I have a query where there are instances where a "phase" starts and ends on the same day - this is calculated as 1 day. If, however, another "phase" starts and ends on the same day against the same ref. no. and period no., then I'd like to calculate this as 0 days.
Example:
**Ref. Period. Phase StDt EndDt**
013 3 KAA 01/01/16 01/01/16 - This is one day
013 3 TAA 02/01/16 03/01/16 - this is 2 days
013 3 KAT 01/01/16 01/01/16 - **would like this to be counted as 0 day**
013 3 TTA 04/04/16 04/04/16 - this is one day
I would like this unique calculation to be done in the data grouped by Ref. And Period numbers. This is a tricky one....
Thanks
Try this.
I am assuming that you are using TSQl (Not sure a you have also tagged SQL.
;WITH cte_result(ID,Ref, Period,Phase,StDt,EndDt) AS
(
SELECT 1,'013' ,3,'KAA',CAST('01/01/16'AS DATETIME),CAST('01/01/16'AS DATETIME) UNION ALL
SELECT 2,'013' ,3,'TAA','01/02/16','01/03/16' UNION ALL
SELECT 3,'013' ,3,'KAT','01/01/16','01/01/16' UNION ALL
SELECT 4,'013' ,3,'TTA','04/04/16','04/04/16')
,cte_PreResult AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY CAST(StDt AS DATE), CAST(EndDt AS DATE) ORDER BY ID) AS [Order],
Ref,
Period,
Phase,
StDt,
EndDt
FROM cte_result
)
SELECT Ref,
Period,
Phase,
StDt,
EndDt,
CASE
WHEN [Order] <> 1
THEN '0 Day(s)'
ELSE CAST(DATEDIFF(dd, StDt, EndDt) + 1 AS VARCHAR(10)) + ' Day(s)'
END AS Comment
FROM cte_PreResult
If there is no ID column then select some column to order by, probably Phase so replace ID with Phase as here ROW_NUMBER() OVER (PARTITION BY StDt,EndDt ORDER BY ID) AS [Order], if there is no candidate column to order by then try this
;WITH cte_result(ID,Ref, Period,Phase,StDt,EndDt) AS
(
SELECT 1,'013' ,3,'KAA',CAST('01/01/16'AS DATETIME),CAST('01/01/16'AS DATETIME) UNION ALL
SELECT 2,'013' ,3,'TAA','01/02/16','01/03/16' UNION ALL
SELECT 3,'013' ,3,'KAT','01/01/16','01/01/16' UNION ALL
SELECT 4,'013' ,3,'TTA','04/04/16','04/04/16')
,cte_PreResult AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY CAST(StDt AS DATE), CAST(EndDt AS DATE) ORDER BY (SELECT NULL)) AS [Order],
Ref,
Period,
Phase,
StDt,
EndDt
FROM cte_result
)
SELECT Ref,
Period,
Phase,
StDt,
EndDt,
CASE
WHEN [Order] <> 1
THEN '0 Day(s)'
ELSE CAST(DATEDIFF(dd, StDt, EndDt) + 1 AS VARCHAR(10)) + ' Day(s)'
END AS Comment
FROM cte_PreResult
This expression should work on the SSRS side:
=IIF(Fields!StartDate.Value=Fields!EndDate.Value AND Fields!Phase.Value <> LOOKUPSET(Fields!StartDate.Value &"_" & Fields!EndDate.Value,Fields!StartDate.Value & "_" & Fields!EndDate.Value,Fields!Phase.Value,"DatasetName").GetValue(0),0,DATEDIFF("D",Fields!StartDate.Value,Fields!EndDate.Value)+1)
It will return a value of 1 for the first phase returned by the dataset. If the phase-date range combinations are not unique within the grouping, this will not work as written, but you should be able to modify accordingly.
Also, if the rows are sorted differently between SSRS and the dataset, it may not be the first row that appears that gets the 1.
The below did the trick! Basically, I'm using count aggregate to count the number of instances where phases start and end on the same day PER Ref and period. Then, for any where there are more than 1, I just use simple case statments to count the first one as 1 and any subsequent ones as 0. I'm creating the below as a subquery in the joins as a left outer join:
LEFT OUTER JOIN
(SELECT TOP (100) PERCENT Period, Ref,
CONVERT(date, PhaseStartDate) AS stdt, CONVERT(date, PhaseEndDate) AS enddt,
COUNT(*)
AS NoOfSameDayPhases,
MIN(PhaseSequence) AS FirstPhSeq
FROM Phases AS Phases_1
WHERE (CONVERT(date, PhaseStartDate) =
CONVERT(date, PhaseEndDate))
GROUP BY VoidPeriod, Ref, CONVERT(date,
PhaseStartDate), CONVERT(date, PhaseEndDate)) AS SameDayPH ON CONVERT(date,
PhaseEndDate) = SameDayPH.enddt AND CONVERT(date,
PhaseStartDate) = SameDayPH.stdt AND
VoidPeriod = SameDayPH.VoidPeriod AND SameDayPH.Ref =
VoidPhases.Ref

How do I change text data to numeric in SQL Server?

SELECT
a.AccountNumber, a.FirstName, a.LastName, a.Address, a.City, a.State,
a.Zip, a.EmailAddress, a.PhoneNumber, a.LastUpdated, a.LastVisit,
a.TotalSales, a.AccountOpened, a.CustomText4 as StoreCode,
CASE
WHEN (a.CustomText1 IS 'JAN') THEN '1'
END AS DOB,
GETDATE() as Extract_date
FROM
database.dbo.Customer a
CustomText1 column has Month data with a text data.I am trying to convert JAN-DEC to Numeric.
CASE WHEN IS '' isn't working.
"IS" is not a valid expression for CASE statement. Check the online doc, you have a couple ways to do it, this is the simplest way, repeat for the rest of the months.
SELECT DOB =
CASE a.CustomText1
WHEN 'JAN' THEN '1'
WHEN 'FEB' THEN '2'
WHEN 'MAR' THEN '3'
ELSE a.CustomText1
END
FROM database.dbo.Customer a
Greg's answer works, but it's over 12 lines. Here's how to do it in one.
SELECT MONTH(CAST('01' + CustomText1 + '00' AS DATE))
FROM dbo.Customer

Use DISTINCT with ORDER BY clause

I want to Alter my following stored procedure
ALTER Procedure [dbo].[spEGCRedemptionReportForMHR]
#DateTo datetime,
#DateFrom datetime,
#MerchantID varchar(11),
#Pan varchar(16)
As
Set #DateTo = #DateTo +1
Select Distinct
Convert(varchar(50),pt.TransactionDate,103) 'TransactionDate',
m.MerchantName1 MerchantName,
m.MerchantAddress Location,
m.MerchantID,
pt.TerminalID,
pt.batchnumber 'Batch #',
pt.SequenceNumber 'Receipt #',
pt.PAN 'Card Number',
c.EmbossName 'Card Holder Name',
Convert(Decimal(10,2),Case when pt.TransactionTypeID=2 then (pt.TotalAmount) end) As 'Points Redeemed',
Convert(Decimal(10,2),Case when pt.TransactionTypeID=2 then (((pt.TotalAmount)/(cc.usdconversionrate))/2) end) as 'Total Payment Amount (AED)', --/cc.USDConversionRate end) As 'Total Amount in AED',
Convert(Decimal(10,2),Case when pt.TransactionTypeID=2 then (((pt.TotalAmount)/(cc.usdconversionrate))/2) -15 end) as 'Total loaded Amount (AED)',
3.00 as 'Procco Share',
Convert(Decimal(10,2),Case when pt.TransactionTypeID=2 then (((pt.TotalAmount)/(cc.usdconversionrate))/2) - 3 end) as 'Settlement Amount'
from POS_Transactions pt
inner join Terminal t on t.TerminalID=pt.TerminalID
inner join Merchant m on m.MerchantID=t.MerchantID
inner join Card c on c.EmbossLine=pt.PAN
inner join Share s on s.MerchantID=m.MerchantID,Currency cc
where IsEmaar =1 and
cc.CurrencyCode='AED'
--and m.isemaarmerchant = 1
and (m.MerchantID=#MerchantID or #MerchantID='-999')
and (pt.TransactionDate>=#datefrom and pt.TransactionDate<=#dateto)
and (pt.PAN=#Pan or #Pan ='-999')
order by pt.TransactionDate
But it throws an error everytime I am trying to execute it
ORDER BY items must appear in the select list if SELECT DISTINCT is specified.
I have already used pt.TransactionDate in my select but still its asking me to include it since it is in my order by clause. What is possibly wrong with my query?
Try:
ORDER BY 'TransactionDate'
The error is not that helpful, but the comment by ughai is correct. The engine is particular about how the column is modified in the SELECT statement, and the exact form must be included in the "ORDER BY."
I have run into this when concatenating columns for output reasons, and it gets ugly fast.
You must
ORDER BY Convert(varchar(50),pt.TransactionDate,103)
If you use GROUP BY instead of DISTINCT to eliminate duplicates then you can include grouped fields whether they're in the select list of not. The downside is you'll have to list all the columns used but this will do it.
GROUP BY
pt.TransactionDate,
m.MerchantName1,
m.MerchantAddress ,
m.MerchantID,
pt.TerminalID,
pt.batchnumber,
pt.SequenceNumber,
pt.PAN ,
c.EmbossName,
pt.TransactionTypeID,
pt.TotalAmount,
cc.usdconversionrate
ORDER BY pt.TransactionDate
Yes the column pt.TransactionDate appears in the SELECT but, in inside the CONVERT function as below:
CONVERT(VARCHAR(50), pt.TransactionDate, 103) 'TransactionDate',
add pt.TransactionDate by itself as an extra column as follows:
...
SELECT DISTINCT
CONVERT(VARCHAR(50), pt.TransactionDate, 103) 'TransactionDate',
pt.TransactionDate, -- <-- here
m.MerchantName1 MerchantName,
m.MerchantAddress Location,
m.MerchantID,
pt.TerminalID,
....

Return detail data related to the corresponding MAX(datetime) value

I have the following dataset using SQL Server 2005:
TABLE NAME: LOG
TYPE TIMESTAMP USERID
Job 26/03/2013 00:24 DED
Job 21/03/2013 02:31 EGA
Sales NULL NULL
Sales NULL NULL
I would like to retrieve the columns TYPE, TIMESTAMP & USERID based on the MAX(timestamp).. i.e the result should be
Job 26/03/2013 00:24 DED
I've tried the following
SELECT max(timestamp), UserID
FROM log
GROUP BY UserID
however that returns all rows.
When I try
SELECT max(timestamp), max(UserID)
FROM log
GROUP BY UserID
that returns
Job 26/03/2013 00:24 EGA
I understand the max(userID) being returned is the max column value sorted alphabetically.
Lastly when i try
SELECT *
FROM log
WHERE timestamp = (SELECTmax(timestamp) FROM log)
i get no result (empty)..
If anyone can help that would be greatly appreciated!
EDIT UPDATE:
If its of any help- i should mention I have multiple tables and would and need to SELECT the USERID with the MAX(timestamp)WHERE TYPE` can equal either ('Job' or 'Sales')..
I am finding this works perfectly:
SELECT Type,
MAX(CASE WHEN log.Type IN ('Job', 'Sales') THEN log.Timestamp ELSE NULL END) As Timestamp,
FROM log
The problem now is when i try:
SELECT Type,
MAX(CASE WHEN log.Type IN ('Job', 'Sales') THEN log.Timestamp ELSE NULL END) As Timestamp,
MAX(CASE WHEN log.Type IN ('Job', 'Sales') THEN log.UserID ELSE NULL END) As UserID,
FROM log
it gives me the max(userID) based on alphabetical order, not based on the max(timeframe)..
I need the MAX(timeframe) AND corresponding UserID when TYPE = JOB or SALES
(to place at the end of a big select query with many other columns).
SAMPLE QUERY UPDATE:
here is the query i am trying to perform.. am terribly sorry for my lack of indepth knowledge!
DECLARE #FROMDATE AS DATETIME, #TODATE AS DATETIME;
SET #FROMDATE = '1 Mar 2013'; SET #TODATE = '31 Mar 2013';
SELECT CASE WHEN country.country_code IN (61,64,673,60,65,679,66,62,92,84,63,91) THEN 'APAC'
WHEN country.country_code IN (81,82,852,853,886) THEN 'EAST ASIA'
WHEN country.country_code IN (44,353,32,45,358,33,49,30,39,352,356,47,351,34,46,31) THEN 'EUROPE'
WHEN country.country_code IN (1,101) THEN 'USA/CANADA' ELSE 'OTHER'
END AS REGION,
info.Status as STATUS, info.Code as CODE,
CONVERT(VARCHAR, info.Start_Date,103) as 'START DATE',
log.Type AS TYPE,
MAX(CASE WHEN log.Type IN ('Job','Sales') THEN log.Timestamp ELSE NULL END) As TIMESTAMP, -- this works fine! :)
MAX(CASE WHEN log.Type IN ('Job','Sales') THEN log.UserID ELSE NULL END) As USERID --having trouble with this! :(
FROM log JOIN info ON (log.id = info.id), country
WHERE info.date BETWEEN #FROMDATE AND #TODATE
AND info.Status NOT LIKE '%NA%'
GROUP BY country.code, info.status, info.code, info.start_date, log.type
i hope that helps guys and thank you again for the AMAZING support. I am guessing this is something simple but i'm missing something..
I created the following sample test table based on your question:
CREATE TABLE Log (TYPE VARCHAR(5) NOT NULL, TIMESTAMP DATETIME NULL, USERID VARCHAR(3) NULL);
INSERT INTO Log
VALUES ('Job', '03/26/2013 00:24', 'DED'), ('Job', '03/21/2013 02:31', 'EGA'), ('Sales', NULL, NULL), ('Sales', NULL, NULL);
I tried your last sample query and it worked properly. Since this query didn't work for you, I wrote the following which should also work and handles scenario that you have more than one row with the same TIMESTAMP value:
WITH BaseData
AS
(SELECT *
, ReverseOrder = ROW_NUMBER() OVER
(ORDER BY TIMESTAMP DESC)
FROM Log)
SELECT *
FROM BaseData
WHERE ReverseOrder = 1;
Check out my answer on another question to grab the first or last row in a table, based on a date column : https://stackoverflow.com/a/10111071/452792
You can try :
DECLARE #FROMDATE AS DATETIME, #TODATE AS DATETIME;
SET #FROMDATE = '1 Mar 2013'; SET #TODATE = '31 Mar 2013';
;WITH LOG_CTE AS
(
SELECT Stuff(Max(Convert(Varchar, Timestamp, 20) + IsNull(Convert(Varchar, Id), '')), 1, 19, '') As Id
, Max(Type) As Type
, Max(timestamp) As Timestamp
, Stuff(Max(Convert(Varchar, Timestamp, 20) + IsNull(Convert(Varchar, UserID), '')), 1, 19, '') As UserID
FROM log
GROUP BY log.Type
HAVING log.Type IN ('JOB','SALES')
)
SELECT CASE WHEN country.country_code IN (61,64,673,60,65,679,66,62,92,84,63,91) THEN 'APAC'
WHEN country.country_code IN (81,82,852,853,886) THEN 'EAST ASIA'
WHEN country.country_code IN (44,353,32,45,358,33,49,30,39,352,356,47,351,34,46,31) THEN 'EUROPE'
WHEN c.country_code IN (1,101) THEN 'USA/CANADA' ELSE 'OTHER'
END AS REGION,
info.Status as STATUS, info.Code as CODE,
CONVERT(VARCHAR, info.Start_Date,103) as 'START DATE',
log.Type AS TYPE,
log.Timestamp,
log.UserID
FROM LOG_CTE log JOIN info ON (log.id = info.id)
WHERE info.date BETWEEN #FROMDATE AND #TODATE
AND info.Status NOT LIKE '%NA%'

Resources