BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
KRHE
Obsidian | Level 7

I use a ole db connection with a select statement and a where condition to retrieve today's data.

The datevar is formated as datetime22.3, numeric, date, 8 lengths in byte. 

Here is the issue though: SAS does not read that date even though i work with other sources that have the exact same format and work just fine.

A a clue is that when writing conditions for another varchar in the same source i had to convert it to varchar even though it already was a var char (as is the datevar).

SAS did simply not read it until I used 'Where cast(y.var as varchar(38)) is not null

 

'/*and cast(x.RowTimestamp as date) = '04OCT2023:05:12:20.380'dt /d not working*/

'/*and cast(x.RowTimestamp as date) = '04OCT2023:05:12:20.380' not working*/

I have also tried to use x.RowTimestamp =%nrbquote('2024-11-11') and that is also not working.

 

This is the first step, the second step is to make it read the macro, but that ofc is also not working, even though the output is exactly the same.

I understand my issue is super complicated and might relate to a thousand issues but perhaps someone has encountered the same issue,

I have built two macros that fetch the current date by different formats:

%let igar=%nrbquote('%sysfunc(putn(%eval(%sysfunc(today())-7),yymmdd10))'); 
%put %nrbquote(&igar.); /*output '2024-04-09' */

%let kebabdate=%sysfunc(intnx(dtday, %sysfunc(datetime()), -7), datetime22.3);
%put &kebabdate.; /* oputput 09APR2024:00:00:00.000 */

1 ACCEPTED SOLUTION

Accepted Solutions
Patrick
Opal | Level 21

@KRHE wrote:
Yes , you are correct, it mssql server and your code is actually working - this is sorcery :P. I will have to deep dive into this a bit later , have a doctors appointment :P.
amazing work

That's what I thought. It's with all error messages always a possibility that the message you're getting is caused by something else that happens earlier and though the error message as such is misleading. In such cases - if you can't fix it "quickly" - you need always to simplify your syntax and debug things step-by-step.

 

Just as a comment to the code you shared:

The way you coded the joins creates too many rows that you then filter via a where clause. I can't test it but believe that with a few tweaks to your code you could get the desired result set without the need to "post filter" the data via where clause.

Below the code version that I believe implements your selection logic but should be a bit more efficient because it reduces data volumes earlier.

proc sql;
  connect to oledb as NBP_data (provider=SQLNCLI11 properties=(
      "Data Source" = "xxxx"
      "Initial Catalog" = "xxx"
      "Integrated Security" = "SSPI"
      "Persist Security" = "False")
    );

  Create Table NBP_data as 

  select  * from connection to NBP_data
    (
      select top 100
        a.id,
        a.Country,
        a.state as kanal,
        b.RowTimestamp as date,
        b.state,
        a.SeApplication_ApplicationType,
        b.loanamount,
        c.Email,
        c.FirstName,
        c.LastName,
        c.Phone,
        c.BenefitGroup_Code as Fackforbund,
        c.Employername,
        c.civilstatus,
        c.discriminator,
        c.employmentcontracttype,
        c.SeApplicant_EmployedSince,
        c.SeApplicant_GrossMonthlyIncome as StatedIncome,
        d.Onboardingtype,
        d.ConfidentialityLevel,
        d.IsActiveCustomer,
        d.Postcode,
        d.InternalCustomerkey as CustNR,
        d.CustomerCountry as LivesIn,
        e.Assettype,
        e.OwnershipType,
        e.INSERTTYPE as CarInstertype,
        f.Discriminator as HasChildren,
        f.INSERTTYPE as childinserttype,
        x.RowTimestamp as NotworkinRowTime,
        x.PropertyVerificationId,
        a.RowTimestamp as WorkinRowTime

      from
        (
          select 
            id,
            Country,
            state,
            SeApplication_ApplicationType,
            RowTimestamp
          from [DDD].[DD].[AP_APPLICATION] 
          where DATEADD(d,0,DATEDIFF(d,0,RowTimestamp)) = '2024-04-10'
        ) a

      left join [DDD].[DD].[AP_SCENARIO] b 
        on b.ApplicationId = a.Id 

      left join [DDD].[DD].[AP_APPLICANT] c 
        on c.ScenarioId = b.id

      left join [DDD].[DD].[CU_CUSTOMERS] d 
        on d.AnonymousCustomerKey= c.AnonymousCustomerKey

      INNER join [DDD].[DD].[PV_APPLICANT] x
        on d.AnonymousCustomerKey=x.AnonymousCustomerKey 
           and x.PropertyVerificationId is not null /* only required if PropertyVerificationId not already defined with NOT NULL constraint */

      left join [DDD].[DD].[AP_SEASSET] e 
        on e.SeApplicantId=a.id

      left join [DDD].[DD].[ap_child] f 
        on f.SeScenarioId=b.id

    );
  disconnect from NBP_data;
quit;

In above code syntax DATEADD(d,0,DATEDIFF(d,0,RowTimestamp)) is used to shift RowTimestamp to the beginning of the day so all rows with date 2024-04-10 will get selected.

Alternative syntax would be: where RowTimestamp>='2024-04-10' and RowTimestamp<'2024-04-11'

View solution in original post

13 REPLIES 13
SASKiwi
PROC Star

I suggest you post the SAS log of an example of the query where you are getting this problem. I've had problems in the past where SQL Server datetimes come through to SAS as character columns and sometimes that can be fixed by using a newer database driver. Please also post a PROC CONTENTS of the SAS dataset being created so we can how the datetime columns are being handled.

Patrick
Opal | Level 21

You need to share your code and ideally SAS log with us. Also tell us which database you're interfacing with.

 

From the looks of it:

The use of the cast() function implies that you're using explicit pass-through SQL where SAS sends all the code as-is to the DB for execution. You need to use DB syntax only in explicit pass-through SQL so '04OCT2023:05:12:20.380'dt will certainly not work.

In many databases conversion of a string to a timestamp would use syntax like to_timestamp('<date string>'<, ...>). Below sample code from the Oracle documentation:

TO_TIMESTAMP ('10-Sep-02 14:10:10.123000', 'DD-Mon-RR HH24:MI:SS.FF')

 

With at least some databases the date string must be enclosed in single quotes and double quotes are not allowed. On the other hand if you want to pass the date string via a SAS macro variable (which gets resolved prior to SAS sending the code to the DB) then you need to use double quotes. One way to get around this is the use of the SAS provided %tslit() macro that will first resolve the macro variable before adding single quotes.

TO_TIMESTAMP (%tslit(&dttm), 'DD-Mon-RR HH24:MI:SS.FF')

 

A a clue is that when writing conditions for another varchar in the same source i had to convert it to varchar even though it already was a var char (as is the datevar).

SAS did simply not read it until I used 'Where cast(y.var as varchar(38)) is not null

Given you're using the cast() function above must also be part of explicit pass-through SQL  syntax fully executed on the database side. As such it's something you also need to investigate on the database side. SAS has nothing to do with it.

 

And last but not least and given your sample using a datetime string with decimals cast(x.RowTimestamp as date) = '04OCT2023:05:12:20.380' 

In case you loaded the data first from SAS to the database including variable RowTimestamp then you would also have to deal with numeric precision. Whenever you transfer floating point numbers with decimals from one platform to another (in general, not only with SAS) you can get small differences in the last bits of the numbers. This is due to differences in the data representation on different platforms (there are many articles and discussions out there around this topic - for example here). 

If you upload data to a database (or download from SAS) and you then want in the target system to compare this data to some fixed value with decimals then you need to either send the data as string and convert to floating point on the target system (cast) OR you need to round() the number to some non-significant position. Let's say you upload a numerical SAS variable with max 6 significant decimals then round the variable on the target side to the 7th or lower decimal.

 

 

KRHE
Obsidian | Level 7

 

options validvarname=any;
options DLDMGACTION=REPAIR;

%let yesterday=%nrbquote('%sysfunc(putn(%eval(%sysfunc(today())-7),yymmdd10))'); /*this is working, */
%put %nrbquote(&yesterday.);

%let kebabdate=%sysfunc(intnx(dtday, %sysfunc(datetime()), -7), datetime22.3); /*this is working, */
%put &kebabdate.;



proc sql;
 connect to oledb as NBP_data (provider=SQLNCLI11 properties=(
 "Data Source" = "xxxx"
 "Initial Catalog" = "xxx"
 "Integrated Security" = "SSPI"
 "Persist Security" = "False") ;
 Create Table NBP_data as 

select  * from connection to NBP_data
(

Select top 100
a.id,
a.Country,
a.state as kanal,
b.RowTimestamp as date,
b.state,
a.SeApplication_ApplicationType,
b.loanamount,
c.Email,
c.FirstName,
c.LastName,
c.Phone,
c.BenefitGroup_Code as Fackforbund,
c.Employername,
c.civilstatus,
c.discriminator,
c.employmentcontracttype,
c.SeApplicant_EmployedSince,
c.SeApplicant_GrossMonthlyIncome as StatedIncome,
d.Onboardingtype,
d.ConfidentialityLevel,
d.IsActiveCustomer,
d.Postcode,
d.InternalCustomerkey as CustNR,
d.CustomerCountry as LivesIn,
e.Assettype,
e.OwnershipType,
e.INSERTTYPE as CarInstertype,
f.Discriminator as HasChildren,
f.INSERTTYPE as childinserttype,
x.RowTimestamp as NotworkinRowTime,
x.PropertyVerificationId,
a.RowTimestamp as WorkinRowTime

  FROM [DDD].[DD].[AP_APPLICATION] a 
  LEFT join [DDD].[DD].[AP_SCENARIO] b on b.ApplicationId = a.Id
LEFT join [DDD].[DD].[AP_APPLICANT] c on c.ScenarioId = b.id
Left join [DDD].[DD].[CU_CUSTOMERS] d on d.AnonymousCustomerKey= c.AnonymousCustomerKey
Left join [DDD].[DD].[AP_SEASSET] e on e.SeApplicantId=a.id
Left join [DDD].[DD].[ap_child] f on f.SeScenarioId=b.id
Left join [DDD].[DD].[PV_APPLICANT] x on d.AnonymousCustomerKey=x.AnonymousCustomerKey 

where a.RowTimestamp = '2024-04-10' /*this is working,  */
*/ where a.RowTimestamp > %nrbquote(&yesterday.) /* this is working */
*/ where x.RowTimestamp > '2024-04-10' /* this is NOT working */

and where cast(x.PropertyVerificationId as varchar(38)) is not null /*(same table as x.Rowtimestamp) */ ); quit;

a.Rowtimestamp is is numeric, date datetime22.3, length 8 bytes = works fine

 

x.Rowtimestamp  is numeric, date , datetime22.3, lenths 8 bytes = does not work
ERROR: Open cursor error: ICommand::Execute failed. : Conversion failed when converting from a character string to uniqueidentifier.

Alphabetic List of Variables and Attributes  # Variable Type Len Format Informat Label  313032
CastedVarcharChar38$38.$38.CastedVarchar 
NotworkinRowTimeNum8DATETIME22.3DATETIME22.3NotworkinRowTime05APR2024:17:34:40.534
WorkingRowTimeNum8DATETIME22.3DATETIME22.3WorkingRowTime05APR2024:05:22:10.094
SASKiwi
PROC Star

You will need to find out how x.RowTimestamp is defined in the database if you can't compare it to a date string in SQL Passthru. This isn't a SAS problem, just a database one. Running this in the SQL tool of your choice would produce the same error.

Patrick
Opal | Level 21

@KRHE It appears you're interfacing with SQL Server. The issue you encounter is on the DB side with DB SQL. SAS has nothing to do with it. You need to investigate the issue on the DB side ideally using a DB client like MS SQL Server Management Studio or the like.

 

The Alphabetic List of Variables and Attributes you show us is to what SAS maps the DB table columns to but these are not the actual DB column types. You need to retrieve this information directly on the DB.

Using SAS code like below could work.

proc sql;
  connect to oledb as NBP_data (provider=SQLNCLI11 properties=(
   "Data Source" = "xxxx"
   "Initial Catalog" = "xxx"
   "Integrated Security" = "SSPI"
   "Persist Security" = "False") ;

  select * from connection to NBP_data
  (
    SELECT 
      ORDINAL_POSITION
      , COLUMN_NAME
      , DATA_TYPE
      , CHARACTER_MAXIMUM_LENGTH
      , IS_NULLABLE
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE table_catalog='DDD' and table_schema='DD' and TABLE_NAME = 'PV_APPLICANT'
    ;
  );
  disconnect from NBP_data;
quit;

 

 

From what SAS maps the columns to it looks like the DB columns are both of type Datetime. But then your syntax should work...

Below executed via MS SQL Server Management Studio.

Patrick_0-1713401783071.png

I suggest for debugging purposes just run your where clause against the one column/table where things appear to return an error. If using SAS you could start with something like below - but ideally you're doing this without SAS using a DB client and eventually with some support from a DBA at your site.

proc sql outobs=5;
  connect to oledb as NBP_data (....);
  select  * from connection to NBP_data
  (
    select RowTimestamp
    from [DDD].[DD].[PV_APPLICANT]
    where RowTimestamp > '2024-04-10'
    ;
  );
  disconnect from NBP_data;
quit;

 

 

 

KRHE
Obsidian | Level 7

Hi everyone and thanks for all the help this far.

I checked the datatypes in sql and x.RowTimestamp is datetime2(7), there is also another one can i can use which is datetime2(3), let call it y.date.

I tried the following in sas:

where x.RowTimestamp = convert(datetime2(7), '2024-03-10 00:00:00.0000000', 121) /* with and without 121 */

/

where y.date=convert(datetime2(3) , ' 2024-03-10 00:00:00.000', 121) /* with and without 121 */

 

but still get the same error:

ERROR: Open cursor error: ICommand::Execute failed. : Conversion failed when converting date and/or time from character string.

 

also tried to input a macro which has the exact same format as the datetimes mentioned above but get the same error.

 

Any ideas?

Patrick
Opal | Level 21

You show us an error message but not the code and log that you executed that created this error.

Can you please try and execute the SQL I've shared already and let us know if this also causes such an error? Below the code once more:

proc sql outobs=5;
  connect to oledb as NBP_data (....);
  select  * from connection to NBP_data
  (
    select RowTimestamp
    from [DDD].[DD].[PV_APPLICANT]
    where RowTimestamp > '2024-04-10'
    ;
  );
  disconnect from NBP_data;
quit;

There is no need to explicitly convert the datetime string to a SQL datetime value. The SQL Server compiler is smart enough to do such a conversion implicitly if you use it as comparison value with a column of type datetime or datetime2. Which format of date/datetime string works is fully documented under Supported string literal formats for datetime2

 

With the SQL Server 2019 version I'm using below code works for all the syntax variations for the Where clause.

The one Where clause that's not in comment uses the datetime string in the ISO 8601 format as per documentation referenced above.

 

Patrick_1-1713520257439.png

 

Which version are you using and can you please also confirm that your database is MS SQL Server.

 

KRHE
Obsidian | Level 7
Yes , you are correct, it mssql server and your code is actually working - this is sorcery :P. I will have to deep dive into this a bit later , have a doctors appointment :P.
amazing work
Patrick
Opal | Level 21

@KRHE wrote:
Yes , you are correct, it mssql server and your code is actually working - this is sorcery :P. I will have to deep dive into this a bit later , have a doctors appointment :P.
amazing work

That's what I thought. It's with all error messages always a possibility that the message you're getting is caused by something else that happens earlier and though the error message as such is misleading. In such cases - if you can't fix it "quickly" - you need always to simplify your syntax and debug things step-by-step.

 

Just as a comment to the code you shared:

The way you coded the joins creates too many rows that you then filter via a where clause. I can't test it but believe that with a few tweaks to your code you could get the desired result set without the need to "post filter" the data via where clause.

Below the code version that I believe implements your selection logic but should be a bit more efficient because it reduces data volumes earlier.

proc sql;
  connect to oledb as NBP_data (provider=SQLNCLI11 properties=(
      "Data Source" = "xxxx"
      "Initial Catalog" = "xxx"
      "Integrated Security" = "SSPI"
      "Persist Security" = "False")
    );

  Create Table NBP_data as 

  select  * from connection to NBP_data
    (
      select top 100
        a.id,
        a.Country,
        a.state as kanal,
        b.RowTimestamp as date,
        b.state,
        a.SeApplication_ApplicationType,
        b.loanamount,
        c.Email,
        c.FirstName,
        c.LastName,
        c.Phone,
        c.BenefitGroup_Code as Fackforbund,
        c.Employername,
        c.civilstatus,
        c.discriminator,
        c.employmentcontracttype,
        c.SeApplicant_EmployedSince,
        c.SeApplicant_GrossMonthlyIncome as StatedIncome,
        d.Onboardingtype,
        d.ConfidentialityLevel,
        d.IsActiveCustomer,
        d.Postcode,
        d.InternalCustomerkey as CustNR,
        d.CustomerCountry as LivesIn,
        e.Assettype,
        e.OwnershipType,
        e.INSERTTYPE as CarInstertype,
        f.Discriminator as HasChildren,
        f.INSERTTYPE as childinserttype,
        x.RowTimestamp as NotworkinRowTime,
        x.PropertyVerificationId,
        a.RowTimestamp as WorkinRowTime

      from
        (
          select 
            id,
            Country,
            state,
            SeApplication_ApplicationType,
            RowTimestamp
          from [DDD].[DD].[AP_APPLICATION] 
          where DATEADD(d,0,DATEDIFF(d,0,RowTimestamp)) = '2024-04-10'
        ) a

      left join [DDD].[DD].[AP_SCENARIO] b 
        on b.ApplicationId = a.Id 

      left join [DDD].[DD].[AP_APPLICANT] c 
        on c.ScenarioId = b.id

      left join [DDD].[DD].[CU_CUSTOMERS] d 
        on d.AnonymousCustomerKey= c.AnonymousCustomerKey

      INNER join [DDD].[DD].[PV_APPLICANT] x
        on d.AnonymousCustomerKey=x.AnonymousCustomerKey 
           and x.PropertyVerificationId is not null /* only required if PropertyVerificationId not already defined with NOT NULL constraint */

      left join [DDD].[DD].[AP_SEASSET] e 
        on e.SeApplicantId=a.id

      left join [DDD].[DD].[ap_child] f 
        on f.SeScenarioId=b.id

    );
  disconnect from NBP_data;
quit;

In above code syntax DATEADD(d,0,DATEDIFF(d,0,RowTimestamp)) is used to shift RowTimestamp to the beginning of the day so all rows with date 2024-04-10 will get selected.

Alternative syntax would be: where RowTimestamp>='2024-04-10' and RowTimestamp<'2024-04-11'

Patrick
Opal | Level 21

@KRHE Just out of curiosity: Could you make your query working in the meantime? And what was the resolution?

KRHE
Obsidian | Level 7

I am still looking into it, I used Patrick code but whenever i add the 'not working date' for output i get the conversion error.

However, the condition on date seems to be working if I do not present that specific date on the output. I have not forgotten about all the help I received, I just want to finalize this before I close the thread. 

KRHE
Obsidian | Level 7

Hi again,

 

So it is fixed now. Previously i stated that ' whenever i add the 'not working timestamp' (asap.rowstimestamp) to output, SAS stated that there is a conversion error' . - This was incorrect, its added. I have also tried adding the other timestamps from the other tables alongside the asap.rowtimestamp, and it also works.

 

As Patrick mentioned, I had to segregate the select statement for that specific table, i.e use a separate select statement within a bracket, I could however not use the where condition in that bracket but had to use it below, i.e in the end after adding up all the joins (see syntax). At that stage I also had to use the convert function i.e conver(asap.rowtimestamp as date) = %NRBquote(&macroToday.) Nrbquote is just used to read the macro since the todays date comes with '' (singel quotes) - without convert it did not work.

 

Thank you @Patrick !

%let igar=%nrbquote('%sysfunc(putn(%eval(%sysfunc(today())-1),yymmdd10))'); 
%put %nrbquote(&igar.); /'output '202404-23' */



proc sql;
 connect to oledb as NBP_data (provider=SQLNCLI11 properties=(
 "Data Source" = "DDD-DDD-PROD"  
 "Initial Catalog" = "DDD"
 "Integrated Security" = "SSPI"
 "Persist Security" = "False") BCP=YES);
 Create Table NBP_data as 

select  * from connection to NBP_data
(

Select distinct
asap.RowTimestamp as Verificationdate, asap.PropertyVerificationId, c.Email, c.FirstName, c.LastName, c.Phone, c.BenefitGroup_Code as Fackforbund, c.Employername, c.civilstatus, c.discriminator, c.employmentcontracttype, c.SeApplicant_EmployedSince, c.SeApplicant_GrossMonthlyIncome as StatedIncome, d.Onboardingtype, d.ConfidentialityLevel, d.IsActiveCustomer, d.Postcode, d.InternalCustomerkey as CustNR, d.CustomerCountry as LivesIn, b.RowTimestamp as date, b.state, b.loanamount, a.id, a.Country, a.state as kanal, a.SeApplication_ApplicationType, f.Discriminator as HasChildren, f.INSERTTYPE as childinserttype from ( select RowTimestamp, PropertyVerificationId, AnonymousCustomerKey from [DDD].[DD].[PV_APPLICANT] /* where asap.RowTimestamp=(select max(xy.RowTimestamp) from [DDD].[DD].[PV_APPLICANT] xy where xy.AnonymousCustomerKey=asap.AnonymousCustomerKey) - not working here */ /* where RowTimestamp = '2024-04-10' - not working here*/ ) asap inner join [DDD].[DD].[AP_APPLICANT] c on c.AnonymousCustomerKey = asap.AnonymousCustomerKey and c.RowTimestamp=(select max(y.RowTimestamp) from [DDD].[DD].[AP_APPLICANT] y where y.AnonymousCustomerKey=c.AnonymousCustomerKey) Left join [DDD].[DD].[CU_CUSTOMERS] d on d.AnonymousCustomerKey=c.AnonymousCustomerKey and d.RowTimestamp=(select max(u.RowTimestamp) from [DDD].[DD].[CU_CUSTOMERS] u where u.AnonymousCustomerKey=d.AnonymousCustomerKey) LEFT join [DDD].[DD].[AP_SCENARIO] b on c.ScenarioId = b.id and b.RowTimestamp=(select max(w.RowTimestamp) from [DDD].[DD].[AP_SCENARIO] w where w.id=b.id) left join [DDD].[DD].[AP_APPLICATION] a on b.ApplicationId = a.Id and a.RowTimestamp=(select max(q.RowTimestamp) from [DDD].[DD].[AP_APPLICATION] q where q.id=a.id) Left join [DDD].[DD].[ap_child] f on f.SeScenarioId=b.id and f.RowTimestamp=(select max(ap.Rowtimestamp) from [DDD].[DD].[ap_child] ap where ap.SeScenarioId=f.SeScenarioId) where cast(asap.RowTimestamp as date) = %nrbquote(&igar.) and asap.RowTimestamp=(select max(xy.RowTimestamp) from [DDD].[DD].[PV_APPLICANT] xy where xy.AnonymousCustomerKey=asap.AnonymousCustomerKey) ); quit;
Patrick
Opal | Level 21

@KRHE Great that you've got now working code. 

 

Based on what you shared I still can't understand why the syntax with a hardcoded date string doesn't work....

Patrick_0-1713915829119.png

...after you confirmed that below code does work.

Patrick_1-1713915897260.png

 

What I did experience in the past is that macro quoting can create issues when used in explicit pass-through SQL.

I can see in your code:

where cast(asap.RowTimestamp as date) = %nrbquote(&igar.)

To avoid potential issues I suggest you amend the code to:

where cast(asap.RowTimestamp as date) = %unquote(%nrbquote(&igar))

 

Alternatively: I've made good experiences with using the SAS supplied %tslit() macro for wrapping single quotes around a resolved macro variable. 

You could generate the date string without quotes and then use %tslit() at the places where you need the string embedded in single quotes.

%let igar=%sysfunc(putn(%eval(%sysfunc(today())-1),yymmdd10.));
%put &igar.; /* output 202404-23 */

proc sql;
....
  where cast(asap.RowTimestamp as date) = %tslit(&igar)
quit;

 

And just as a personal preference: I normally try to avoid nesting of more than one %sysfunc() just because I find it hard to read. 

%let igar=%sysfunc(putn(%eval(%sysfunc(today())-1),yymmdd10));

I consider below alternative code much easier to read and maintain.

data _null_;
  dt=today()-1;
  call symputx('igar',put(dt,yymmdd10.));
run;
%put &=igar;

 

 

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 13 replies
  • 786 views
  • 3 likes
  • 3 in conversation