<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: Performance, joining two tables 14m rows in SAS Data Management</title>
    <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317897#M9130</link>
    <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/13674"&gt;@LinusH&lt;/a&gt;Thanks for your reply. No, YEAR is not in ERG &lt;span class="lia-unicode-emoji" title=":slightly_smiling_face:"&gt;🙂&lt;/span&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/42345"&gt;@rogerjdeangelis&lt;/a&gt;indeed there are about 40 variables (some with lots of texts) in those&lt;BR /&gt;datasets BUT for my particular query I only use year, office, country, office_name,car,car_name,residue,residuename&lt;BR /&gt;and they are not patriculary long or complex variables.&lt;BR /&gt;Size statistics are attached. I canceled the Proc SQL so cant really tell you how much it would have&lt;BR /&gt;taken. There result set would have hadto count 14m rows and resulted in maybe 400 grouped by&lt;BR /&gt;variables like year, office, country, office_name,car,car_name,residue,residuename.&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;&lt;BR /&gt;&lt;IMG src="https://communities.sas.com/t5/image/serverpage/image-id/13245i88EED36229ACE526/image-size/large?v=1.0&amp;amp;px=600" border="0" alt="proc_content.png" title="proc_content.png" /&gt;</description>
    <pubDate>Fri, 09 Dec 2016 13:53:42 GMT</pubDate>
    <dc:creator>PhilipH</dc:creator>
    <dc:date>2016-12-09T13:53:42Z</dc:date>
    <item>
      <title>Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317847#M9121</link>
      <description>&lt;P&gt;Hi SAS Performance Experts,&lt;BR /&gt;I am doing a join of two SAS datasets.&lt;BR /&gt;&lt;BR /&gt;SASDWHB.DMPERG ERG&amp;nbsp; about 160 Gb and 110 m rows&lt;BR /&gt;&lt;BR /&gt;SASDWHB.DMPSTA STA about 17 Gb and 6m rows.&lt;BR /&gt;&lt;BR /&gt;Doing and inner join for a partricular year results in about 14m rows.&lt;BR /&gt;&lt;BR /&gt;Our virutal sas server has 6vCPUs and 64 GB vRAM running SUSE Linux v11 64 bit&lt;BR /&gt;and yet the below query runs since 3 hours but I expect something like&lt;BR /&gt;3 minuts maximum.&lt;BR /&gt;Where do we start to analyse why the performance is so bad?&lt;BR /&gt;&lt;BR /&gt;we do use SASBASE library.&lt;BR /&gt;&lt;BR /&gt;proc sql;&lt;BR /&gt;select year, office, country, office_name,car,car_name,residue,residuename,&lt;BR /&gt;count(distinct STA.sta_id), count (distinct ERG.erg_id), count(distinct STA.cartypeID)&lt;BR /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;FROM SASDWHB.DMPSTA STA&lt;BR /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;INNER JOIN SASDWHB.DMPERG ERG&lt;BR /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ON STA.STA_ID = ERG.STA_ID&lt;BR /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;where year='2015'&lt;BR /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;;&lt;BR /&gt;QUIT;&lt;BR /&gt;&lt;BR /&gt;I am the only person using the server right now.&lt;/P&gt;&lt;BR /&gt;&lt;IMG src="https://communities.sas.com/t5/image/serverpage/image-id/13242i53550F3C63E6A23A/image-size/large?v=1.0&amp;amp;px=600" border="0" alt="1.png" title="1.png" /&gt;&lt;IMG src="https://communities.sas.com/t5/image/serverpage/image-id/13243i4E40295A6784523E/image-size/large?v=1.0&amp;amp;px=600" border="0" alt="2.png" title="2.png" /&gt;</description>
      <pubDate>Fri, 09 Dec 2016 10:32:29 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317847#M9121</guid>
      <dc:creator>PhilipH</dc:creator>
      <dc:date>2016-12-09T10:32:29Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317860#M9122</link>
      <description>&lt;P&gt;You do a join and use 3 count distincts.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;This is one of the usual cases where SQL performance in SAS goes from bad to incredibly slow (on the order of watching paint dry and grass growing)&lt;/P&gt;
&lt;P&gt;(Four different sorts need to be done, and remerging all counts back into the dataset)&lt;/P&gt;
&lt;P&gt;All this is done in one utility file that includes everything (kitchen sink!) and causes massive load on the WORK disk(s)&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;My suggestion:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Sort each data set separately by sta_id (if not yet sorted that way)&lt;/P&gt;
&lt;P&gt;When sorting the dataset that contains year, use a keep= and where= dataset option to subset by year and reduce observation size, and create a new intermediate dataset. (keep= on both datasets, if unneeded columns are present)&lt;/P&gt;
&lt;P&gt;Merge in a datastep. While doing that, you can already create a secondary dataset with the count of sta_id.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Did you omit a group by deliberately, or is that a mistake?&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;And one final consideration: you expect 3 minutes (180 seconds) for a process that requires at least 300 GB to be moved physically. Is your I/O subsystem capable of &amp;gt; 2GB/sec throughput when doing random read/writes?&lt;/P&gt;</description>
      <pubDate>Fri, 09 Dec 2016 11:10:13 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317860#M9122</guid>
      <dc:creator>Kurt_Bremser</dc:creator>
      <dc:date>2016-12-09T11:10:13Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317866#M9123</link>
      <description>Hi,&lt;BR /&gt;Thanks for the fast reply.&lt;BR /&gt;Yes, the GROUP BY was cut off while transfering the statement. I have no idea what aour I/O subsystem in capable of. I come from the Oracle world and Oracle would use the filter in the WHERE clause first and work with the 14m rows which are then handled - at least in my experience much faster than what I see here in SAS but then...as you suggest. Using proc sql might not be the best way to go forward here. I should use the sas base syntax. I give it a try.&lt;BR /&gt;The dataset is sorted by year and I would start working only with the 2015 data before sorting by IDs. Does that make sense?</description>
      <pubDate>Fri, 09 Dec 2016 11:22:45 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317866#M9123</guid>
      <dc:creator>PhilipH</dc:creator>
      <dc:date>2016-12-09T11:22:45Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317867#M9124</link>
      <description>I also wonder why my second screen print shows a max free memory of 1,2 gb.</description>
      <pubDate>Fri, 09 Dec 2016 11:31:39 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317867#M9124</guid>
      <dc:creator>PhilipH</dc:creator>
      <dc:date>2016-12-09T11:31:39Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317872#M9125</link>
      <description>&lt;BLOCKQUOTE&gt;&lt;HR /&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/84122"&gt;@PhilipH&lt;/a&gt; wrote:&lt;BR /&gt;Hi,&lt;BR /&gt;Thanks for the fast reply.&lt;BR /&gt;Yes, the GROUP BY was cut off while transfering the statement. I have no idea what aour I/O subsystem in capable of. I come from the Oracle world and Oracle would use the filter in the WHERE clause first and work with the 14m rows which are then handled - at least in my experience much faster than what I see here in SAS but then...as you suggest. Using proc sql might not be the best way to go forward here. I should use the sas base syntax. I give it a try.&lt;BR /&gt;The dataset is sorted by year and I would start working only with the 2015 data before sorting by IDs. Does that make sense?&lt;HR /&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Yes. Reducing data as early as possible in the analytic chain is the first key to performance tuning. Both vertically (where conditions or subsetting if) and horizontally (keep/drop).&lt;/P&gt;
&lt;P&gt;Depending on your data structure, using the compress=yes dataset option will at least reduce the disk space needed for storage; utilty files (sort, SQL) will always be uncompressed.&lt;/P&gt;
&lt;P&gt;While RDBMS systems tend to have the whole DB in memory (or in a structure that is close to that), with lots of defined indexes, SAS keeps tables in individual files on disk. Indexes in SAS won't help if they can't be used to extract a rather tiny slice out of the dataset.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;My first step would be&lt;/P&gt;
&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;proc sort
  data=table_a (
    keep=year stat_id /* other variables */
    where=(year = 2015)
  )
  out=int_a
;
by stat_id;
run;&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;and then proceed from there&lt;/P&gt;</description>
      <pubDate>Fri, 09 Dec 2016 12:03:41 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317872#M9125</guid>
      <dc:creator>Kurt_Bremser</dc:creator>
      <dc:date>2016-12-09T12:03:41Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317875#M9126</link>
      <description>&lt;P&gt;If YEAR is in ERG I would strongly suggest moving to SPDE as test at least. This since SPDE has a more&amp;nbsp;powerful index technology than base SAS (make sure YEAR is indexed). Also, SPDE has better I/O threading possibilities (given that your current constraint isn't I/O).&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;PROC SQL _method;&lt;/P&gt;
&lt;P&gt;will give you the chosen SQL plan.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;OPTIONS FULLSTIMER;&lt;/P&gt;
&lt;P&gt;will give somewhat detail information&amp;nbsp;about resource consumption.&lt;/P&gt;
&lt;P&gt;Be sure to set MEMSIZE and SORTSIZE to appropriate&amp;nbsp;values (utilizing your physical memory as&amp;nbsp;much as possible - join means sorting, and is memory&amp;nbsp;intense).&lt;/P&gt;</description>
      <pubDate>Fri, 09 Dec 2016 12:22:54 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317875#M9126</guid>
      <dc:creator>LinusH</dc:creator>
      <dc:date>2016-12-09T12:22:54Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317882#M9127</link>
      <description>&lt;P&gt;There may be issues with your data structure. Could you provide the two proc contents and the join key.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;A 17gb dataset with only 6 million rows means that the record length is almost 3,000 bytes.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;It is a commmon practice to use codes for long text and even 8 byte bymerics to reduce the width of records.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Both datasets seem very fat?&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;How many rows and what is the width of the resultant dataset.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Fri, 09 Dec 2016 12:43:53 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317882#M9127</guid>
      <dc:creator>rogerjdeangelis</dc:creator>
      <dc:date>2016-12-09T12:43:53Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317884#M9128</link>
      <description>Fullstimer statistics would also be usefull</description>
      <pubDate>Fri, 09 Dec 2016 12:45:46 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317884#M9128</guid>
      <dc:creator>rogerjdeangelis</dc:creator>
      <dc:date>2016-12-09T12:45:46Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317893#M9129</link>
      <description>&lt;P&gt;I think it is best for Hash Table.&lt;/P&gt;
&lt;P&gt;Can you post an example better be data step code, and don't forget post the output.&lt;/P&gt;</description>
      <pubDate>Fri, 09 Dec 2016 13:40:35 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317893#M9129</guid>
      <dc:creator>Ksharp</dc:creator>
      <dc:date>2016-12-09T13:40:35Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317897#M9130</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/13674"&gt;@LinusH&lt;/a&gt;Thanks for your reply. No, YEAR is not in ERG &lt;span class="lia-unicode-emoji" title=":slightly_smiling_face:"&gt;🙂&lt;/span&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/42345"&gt;@rogerjdeangelis&lt;/a&gt;indeed there are about 40 variables (some with lots of texts) in those&lt;BR /&gt;datasets BUT for my particular query I only use year, office, country, office_name,car,car_name,residue,residuename&lt;BR /&gt;and they are not patriculary long or complex variables.&lt;BR /&gt;Size statistics are attached. I canceled the Proc SQL so cant really tell you how much it would have&lt;BR /&gt;taken. There result set would have hadto count 14m rows and resulted in maybe 400 grouped by&lt;BR /&gt;variables like year, office, country, office_name,car,car_name,residue,residuename.&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;&lt;BR /&gt;&lt;IMG src="https://communities.sas.com/t5/image/serverpage/image-id/13245i88EED36229ACE526/image-size/large?v=1.0&amp;amp;px=600" border="0" alt="proc_content.png" title="proc_content.png" /&gt;</description>
      <pubDate>Fri, 09 Dec 2016 13:53:42 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317897#M9130</guid>
      <dc:creator>PhilipH</dc:creator>
      <dc:date>2016-12-09T13:53:42Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317898#M9131</link>
      <description>Hi Ksharp. This one of hundreds of queries I have to write. I cant start creating hash tables just for a simple query like this.</description>
      <pubDate>Fri, 09 Dec 2016 13:52:51 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317898#M9131</guid>
      <dc:creator>PhilipH</dc:creator>
      <dc:date>2016-12-09T13:52:51Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317924#M9132</link>
      <description>&lt;P&gt;I have to admit after my Windows client just crashed (see last proc sql) I am in&lt;BR /&gt;a bit of a faith crisis here. The whole exercise is just a test and does not&lt;BR /&gt;even closely represent to the actual work load of the server once 20 developers&lt;BR /&gt;starting working with the data and the system goes down left right and centre.&lt;BR /&gt;Why are we using SAS again?&lt;BR /&gt;I have been developing 8 years with Oracle and SQL Server and never had any of those issues.&lt;BR /&gt;I feel worried that I have to do all this pre-sorting and merging. Future&lt;BR /&gt;developers and end-users will shoot me if I have to tell them that they have to do that &lt;span class="lia-unicode-emoji" title=":grinning_face_with_smiling_eyes:"&gt;😄&lt;/span&gt;&lt;BR /&gt;I try to run the statements on Monday again.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;&lt;PRE&gt;proc sort /*1 minute*/
  data=DMPSTAM (
    keep=sta_id year  office  country  office_name car car_name cartypeID
    where=(year = '2015')
  )
  out=DMPSTA_SORTED
;
by sta_id;
run;

proc sort /*7 minutes*/
  data=DMPERG (
    keep=sta_id erg_id residue residuename
  )
  out=DMPERG_SORTED
;
by sta_id;
run;


/*client EG crashed after trying to load over 6gb of data into memory- the code executed on the server*/
OPTIONS MSGLEVEL=I;
proc sql _METHOD;
select year, office, country, office_name,car,car_name,residue,residuename,
count(distinct STAMM.sta_id), count (distinct erg_id), count(distinct cartypeid)
    FROM DMPSTA_SORTED STAMM
    INNER JOIN DMPERG_SORTED ERG
            ON STAMM.STA_ID = ERG.STA_ID
    where year ='2015'
    group by year, office, country, office_name,residue,residuename,car,car_name
    ;
QUIT;&lt;/PRE&gt;</description>
      <pubDate>Fri, 09 Dec 2016 15:51:14 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317924#M9132</guid>
      <dc:creator>PhilipH</dc:creator>
      <dc:date>2016-12-09T15:51:14Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317949#M9133</link>
      <description>&lt;PRE&gt;&lt;CODE class=" language-sas"&gt;This does not solve your problem but I
am having trouble coming with example data.

I was able to join a 160,000,000 11 variable table
with a 6,000,000 2 variable table in under 2 minutes.

I have a very slow old 2008 computer with DDR2 ram.
Newer computers should cut the time in half.

 I was unable to create a 14m result?
Do not see a need to parallelize the code(yet)

/*  60k result is a far cry from 14m but I don't see how to get 14m
My resulting table

Result of join of 160,000,000 and 6,000,000
NOTE: Table SD1.RES created, with 60000 rows and 10 columns.

261 !  quit;
NOTE: PROCEDURE SQL used (Total process time):
      real time           2:03.27
      cpu time            3:19.24
*/

Maybe you only need one variable in the potential 1:1 relationships?

Here are my assumed cardinalities

3 Years

4 Countries

10,000 erg_ids ( algorithm is relatively insensitive to this variable)

1,000 different offices  (assume office_name is 1:1 with office)
1,000 different office names  (assume office_name is 1:1 with office)

100 different cars  1:1 car names
100 different car names

400 redidues  1:1 with residue name
400 residue name

INPUTS

Up to 40 obs from spde.dmperg160m total obs=160,000,000

                                 OFFICE_
  STA_ID    OFFICE    COUNTRY     NAME      CAR     CAR_NAME    RESIDUE    RESIDUENAME    ERG_ID    YEAR

     1      A049;       C01       D049;     E049      F049       G04C         H04C           2      2005
     1      A0324       C02       D0324     E032      F03F       G03&amp;lt;         H03&amp;lt;          61      2004
     1      A0348       C02       D0348     E034      F03&amp;gt;       G03&amp;gt;         H03&amp;gt;          81      2003
     1      A0;32       C04       D0;32     E0;3      F0;3       G0;=         H0;=          49      2004
     1      A0252       C01       D0252     E025      F025       G025         H025          80      2005
     1      A035:       C02       D035:     E035      F03S       G03?         H03?          21      2003
     1      A053:       C02       D053:     E053      F05=       G05=         H05=          83      2005
     1      A069:       C04       D069:     E069      F069       G0@9         H0@9          54      2003

.....

Up to 40 obs from spde.dmpsta17m total obs=6,000,000

                     CARTYPE_
    Obs    STA_ID       ID

      1       1         33
      2       2         44
      3       3         55
      4       4         66
      5       5         77
      6       6         88
      7       7         99
      8       8         ::
      9       9         ;;
     10      10         &amp;lt;&amp;lt;


WANT ( Cannot get 14m which may imply exact duplicate records)

Up to 40 obs SD1.RES total obs=60,000

                              OFFICE_                                                                        CARTYPE_
  Obs    OFFICE    COUNTRY     NAME      CAR     CAR_NAME    RESIDUE    RESIDUENAME    STA_IDS    ERG_IDS       IDS

    1    A0222       C01       D0222     E022      F022       G022         H022          280         2          43
    2    A0222       C01       D0222     E022      F022       G02&amp;lt;         H02&amp;lt;          282         2          42
    3    A0222       C01       D0222     E022      F022       G0&amp;lt;2         H0&amp;lt;2          152         1          38
    4    A0222       C01       D0222     E022      F022       G0&amp;lt;&amp;lt;         H0&amp;lt;&amp;lt;          170         1          37
    5    A0222       C01       D0222     E022      F02&amp;lt;       G022         H022          318         2          42
    6    A0222       C01       D0222     E022      F02&amp;lt;       G02&amp;lt;         H02&amp;lt;          306         2          46
    7    A0222       C01       D0222     E022      F02&amp;lt;       G0&amp;lt;2         H0&amp;lt;2          159         1          39
    8    A0222       C01       D0222     E022      F02&amp;lt;       G0&amp;lt;&amp;lt;         H0&amp;lt;&amp;lt;          140         1          31
    9    A0222       C01       D0222     E022      F02F       G022         H022          275         2          45
   10    A0222       C01       D0222     E022      F02F       G02&amp;lt;         H02&amp;lt;          274         2          42
   11    A0222       C01       D0222     E022      F02F       G0&amp;lt;2         H0&amp;lt;2          156         1          35
   12    A0222       C01       D0222     E022      F02F       G0&amp;lt;&amp;lt;         H0&amp;lt;&amp;lt;          145         1          35
   13    A0222       C01       D0222     E022      F02P       G022         H022          303         2          43
   14    A0222       C01       D0222     E022      F02P       G02&amp;lt;         H02&amp;lt;          301         2          46
   15    A0222       C01       D0222     E022      F02P       G0&amp;lt;2         H0&amp;lt;2          146         1          36
   16    A0222       C01       D0222     E022      F02P       G0&amp;lt;&amp;lt;         H0&amp;lt;&amp;lt;          173         1          40


WORKING CODE
============

  group
     by  r.office
        ,r.country
        ,r.office_name
        ,r.car
        ,r.car_name
        ,r.residue
        ,r.residuename

FULL SOLUTION
=============

libname spde spde
 ('c:\wrk\spde_c','d:\wrk\spde_d','e:\wrk\spde_e','g:\wrk\spde_g','h:\wrk\spde_h')
    metapath =('c:\wrk\spde_c\metadata')
    indexpath=(
          'c:\wrk\spde_c'
          ,'d:\wrk\spde_d'
          ,'e:\wrk\spde_e'
          ,'g:\wrk\spde_g'
          ,'h:\wrk\spde_h')

    datapath =(
          'c:\wrk\spde_c'
          ,'d:\wrk\spde_d'
          ,'e:\wrk\spde_e'
          ,'g:\wrk\spde_g'
          ,'h:\wrk\spde_h')
    partsize=500m
;

proc datasets lib=spde kill;
run;quit;

* CREATE INPUT;

data spde.dmperg160m (drop=ran: rec sortedby=country index=(country));
  retain sta_id 0;
  length
      office  $5
      country $3
      office_name $5
      car $4
      car_name $4
      residue $4
      residuename $4
      erg_id $4;
  do rec=1 to 40000000;
    do country='C01','C02','C02','C04';
      if mod(rec,10) = 0 then sta_id=sta_id+1;
      ran=int(100*uniform(5731));
      ran1=int(100*uniform(5731));
      ran2=int(100*uniform(5731));
      year=put(2003+mod(ran,3),4.);
      office           =  cats('A0',byte(mod(ran,10)+50),byte(mod(ran1,10)+50),byte(mod(ran2,10)+50));
      office_name      =  cats('D0',byte(mod(ran,10)+50),byte(mod(ran1,10)+50),byte(mod(ran2,10)+50));
      car              =  cats('E0',byte(mod(ran,10)+50),byte(mod(ran1,10)+50));
      car_name         =  cats('F0',byte(mod(ran,10)+50),byte(mod(ran1,50)+50));
      residue          =  cats('G0',byte(mod(ran,20)+50),byte(mod(ran1,20)+50));
      residuename      =  cats('H0',byte(mod(ran,20)+50),byte(mod(ran1,20)+50));
      erg_id           =  put(mod(ran,9999),4.);
      output;
   end;
  end;

run;quit;

/*

NOTE: The data set SPDE.DMPERG160M has 160000000 observations and 11 variables.
NOTE: DATA statement used (Total process time):
      real time           4:47.34
      user cpu time       7:01.88
      system cpu time     4:11.58
      memory              63209.31k
      OS Memory           89692.00k
      Timestamp           12/09/2016 10:24:40 AM
      Step Count                        145  Switch Count  9766
*/

data spde.dmpsta17m(index=(sta_id));
  retain sta_id 0;
  length cartype_id $2;
  do sta_id=1 to 6000000;
      cartype_id       = cats(byte(mod(sta_id,50)+50),byte(mod(sta_id,50)+50));
      output;
  end;
run;quit;

/*
NOTE: The data set SPDE.DMPSTA17M has 6000000 observations and 2 variables.
NOTE: DATA statement used (Total process time):
      real time           3.54 seconds
      cpu time            5.67 seconds

1280!     quit;
*/

proc sql;
  create
    table sd1.res as
  select
     r.office
    ,r.country
    ,r.office_name
    ,r.car
    ,r.car_name
    ,r.residue
    ,r.residuename
    ,count(distinct l.sta_id)      as sta_ids
    ,count (distinct r.erg_id)     as erg_ids
    ,count(distinct l.cartype_ID)  as cartype_IDs
  from
     spde.dmpsta17m as l
    ,spde.dmperg160m as r
  where
          r.year='2005'
     and  l.sta_id = r.sta_id
  group
     by  r.office
        ,r.country
        ,r.office_name
        ,r.car
        ,r.car_name
        ,r.residue
        ,r.residuename
;quit;

NOTE: Table SD1.RES created, with 60000 rows and 10 columns.

261 !  quit;
NOTE: PROCEDURE SQL used (Total process time):
      real time           2:03.27
      cpu time            3:19.24

&lt;/CODE&gt;&lt;/PRE&gt;</description>
      <pubDate>Fri, 09 Dec 2016 17:40:40 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317949#M9133</guid>
      <dc:creator>rogerjdeangelis</dc:creator>
      <dc:date>2016-12-09T17:40:40Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317954#M9134</link>
      <description>Noticed an error which will just make the algoritm faster&lt;BR /&gt;&lt;BR /&gt;car = cats('E0',byte(mod(ran,10)+50),byte(mod(ran1,10)+50));&lt;BR /&gt;car_name = cats('F0',byte(mod(ran,10)+50),byte(mod(ran1,50)+50));&lt;BR /&gt;&lt;BR /&gt;should be&lt;BR /&gt;car = cats('E0',byte(mod(ran,10)+50),byte(mod(ran1,10)+50));&lt;BR /&gt;car_name = cats('F0',byte(mod(ran,10)+50),byte(mod(ran1,10)+50));&lt;BR /&gt;&lt;BR /&gt;Car and vcar_name are not 1:1</description>
      <pubDate>Fri, 09 Dec 2016 17:47:12 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/317954#M9134</guid>
      <dc:creator>rogerjdeangelis</dc:creator>
      <dc:date>2016-12-09T17:47:12Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318070#M9136</link>
      <description>&lt;PRE&gt;
Make indexes for those table might give you faster.
And add options before SQL might give you faster too.

options bufno=100 bufsize=128k cpucount=10;


&lt;/PRE&gt;</description>
      <pubDate>Sat, 10 Dec 2016 10:48:04 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318070#M9136</guid>
      <dc:creator>Ksharp</dc:creator>
      <dc:date>2016-12-10T10:48:04Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318098#M9137</link>
      <description>&lt;P&gt;&lt;EM&gt;client EG crashed after trying to load over 6gb of data into memory&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;Always have a &lt;STRONG&gt;create table&lt;/STRONG&gt; in your SQL as else you're creating a report which gets transferred in full to the client side. If you create a Work table in SAS then data remains on the server and if you open the table in EG then only small slices of data get transfered to the client side.&lt;/P&gt;
&lt;P&gt;In EG under Options/Results I also always untick "Automatically open data or results when generated". If you open the data then only a sub-set gets transferred to the client side (as it would when you run code using SQL Developer or the like).&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;If coming from using Oracle then yes, the SAS SQL compiler isn't as powerfull as Oracle's and with the data volumes you're dealing with my ideal setup would be to have the data in a database like Oracle accessed via SAS and then use the best from both worlds.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;As with any other environment/application: You want to make sure that it's configured to optimally support what you're doing. With SAS what's important when dealing with high data volumes is to have WORK and UTILLOC pointed to a disk with I/O as fast as possible. Ideally WORK and UTILLOC are also on different disks.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;If you join tables then there is always a sort involved (and it's not different with a DBMS). Proc SQL will save the temporary files in the UTILLOC area and the sort is multi-threaded (I believe).&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;When joining a big table with one or several&amp;nbsp;small tables then one technique in SAS is to use a data step with a hash lookup to the small table.&amp;nbsp;This removes the need to sort the big table and that's the reason why it's often very fast.&lt;/P&gt;
&lt;P&gt;If the look-up is only a key/value pair then creating and using a SAS Format is another very efficient way of doing things.&lt;/P&gt;
&lt;P&gt;You might be more used to SQL than data step syntax; it's not that hard though to code a SAS hash table lookup as part of a SAS data step once you've got used to the syntax.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Of course creating and using indexes can also speed up things dramatically. Unlike Oracle SAS can't make partial use of an index or use multiple indexes at the same time for the same join condition, so the index must be created over exactly the variables you're using in your join (there can be more variables in the join condition and SAS will then just have table scans on the remaining sub-set after using the index).&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;And yes, I'd also expect that a WHERE clause gets applied before the join and sub-sets the data sources. On a side note: I'd always use the alias together with variable names as it makes code just so much more readable.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;It's no more proper SQL but alternative syntax to sub-set data when using SAS files is:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Proc SQL;&lt;/P&gt;
&lt;P&gt;create table ... as&lt;/P&gt;
&lt;P&gt;from&lt;/P&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp;libref.mytable&lt;STRONG&gt;&lt;EM&gt;(where=(year='2015'))&lt;/EM&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp; &amp;nbsp;.....&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Sun, 11 Dec 2016 01:58:14 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318098#M9137</guid>
      <dc:creator>Patrick</dc:creator>
      <dc:date>2016-12-11T01:58:14Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318215#M9138</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/12447"&gt;@Patrick&lt;/a&gt;&lt;BR /&gt;&lt;BR /&gt;yes, indeed I forgot the CREATE TABLE. Thanks for that.&lt;BR /&gt;&lt;BR /&gt;I have used Hash Tables and DI Studio as well to create the two big tables&lt;BR /&gt;in the first place (those two big tables are made of 30 join) BUT looking&lt;BR /&gt;through a "small" subset of those two tables I can not tell developers to create&lt;BR /&gt;HASH objects in between just to do the join. It is just bad usability and&lt;BR /&gt;they wont accept it.&lt;BR /&gt;&lt;BR /&gt;I would use alias but this is just a tiny test of what is about to hit our SAS server&lt;BR /&gt;when 20+ developers start working with those two big tables.&lt;BR /&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/42345"&gt;@rogerjdeangelis&lt;/a&gt;. Thank you very much for your example!&lt;BR /&gt;Great work!&lt;BR /&gt;I can imagine it is trouble coming&lt;BR /&gt;up with 110m rows of example data. That is why this remains&lt;BR /&gt;a theoretical discusison about concepts and server settings.&lt;BR /&gt;&lt;BR /&gt;14m is the data for 2015. That is a total of 14m rows that are then counted (distinct) and&lt;BR /&gt;are aggregated to maybe 400 rows - cant tell for sure because the result has not yet&lt;BR /&gt;been produced by myself &lt;span class="lia-unicode-emoji" title=":slightly_smiling_face:"&gt;🙂&lt;/span&gt;&lt;BR /&gt;&lt;BR /&gt;Of course. I actually only need 11 variables.&lt;BR /&gt;&lt;BR /&gt;year, office, country, office_name,car,car_name,residue,residuename,&lt;BR /&gt;count(distinct STAMM.sta_id), count (distinct erg_id), count(distinct cartypeid)&lt;BR /&gt;&lt;BR /&gt;one STA_ID might have 1 to n ERG_IDs and one STA_ID might have 1 to n CARTYPE_IDs&lt;BR /&gt;&lt;BR /&gt;The names of offices and cars and residues is hard to predict. pick anything.&lt;BR /&gt;there are no exact duplicated ERG_ID is a GUID and I do a count distinct on sta_id and cartypeID.&lt;/P&gt;</description>
      <pubDate>Mon, 12 Dec 2016 07:35:25 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318215#M9138</guid>
      <dc:creator>PhilipH</dc:creator>
      <dc:date>2016-12-12T07:35:25Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318267#M9141</link>
      <description>&lt;P&gt;&lt;a href="https://communities.sas.com/t5/user/viewprofilepage/user-id/84122"&gt;@PhilipH&lt;/a&gt;&lt;/P&gt;
&lt;P&gt;I obviously can't know what you're dealing with. 20+ developers sounds like a very big SAS project; or are this 20 power users consuming some sort of analytical&amp;nbsp;base table(s) for modelling and reporting purposes?&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;I'd also ask the question why you have to develop with full data volumes. This increases development time often substantially. You certainly want then full volumes as early in the test cycle as possible right after unit testing (ST); or at least "representative" volumes in order to detect performance issues as early as possible.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;If your table is that central, important and huge then I'd have a performance optimized focus which always requires some extra coding - and I'd honestly wouldn't really care too much about some developer's preferences unless this opinion helps to better meet the requirements.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;"those two big tables are made of 30 join"&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;Hopefully not in a single big SQL. The SAS SQL compiler can get "confused" in such situations and performance can become really bad. You normally don't want to join more than 3 to 4 tables at once.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;"&lt;SPAN&gt;It is just bad usability and&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;they wont accept it.&lt;/SPAN&gt;"&lt;/P&gt;
&lt;P&gt;I disagree with you and can only hope that at least some of your developers are on senior SAS coding level and they are not all database developers which got some SAS upskilling; else you're up for&amp;nbsp;a lot of trouble.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;BTW: Should your usage of the big table mainly be in yearly slices then consider to store the data in separate yearly SAS files. You can always create a view over these files for access to the whole data at once. As already proposed using SPDE could also help; especially if the main way of accessing the data will be via SQL code.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 12 Dec 2016 13:44:30 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318267#M9141</guid>
      <dc:creator>Patrick</dc:creator>
      <dc:date>2016-12-12T13:44:30Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318274#M9142</link>
      <description>&lt;P&gt;As a side note and this may not hep.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Count distinct is much faster and can be both &amp;nbsp;muti-threaded and multitasked , if you use a group by.&lt;/P&gt;&lt;P&gt;Even if you do not want the distincts grouped. You can drop the grouping on a second count distinct.&lt;/P&gt;&lt;P&gt;You can even use a group by with a numeric primary key using the mod function.&lt;/P&gt;&lt;P&gt;This often reduces the skewness ( each task or thread processes the same number of records).&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;This is how Oracle and Terdata process big data, with PERL running SAS batch you should be able to assign a core to each&lt;/P&gt;&lt;P&gt;task. I use an inexpensive Dell T7400 8 core workstation(circa 2008) with 64gb abd dual raid 0 arrays too outperform most&lt;/P&gt;&lt;P&gt;servers on small datasets ie (small data occurs when no single permanent or temporary table exceeds 1TB).&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;SAS worstation(SAS calls it PC-SAS) allows up to 16 cores and 32gb and includes SPDE (data partitioning and parallel processing &amp;nbsp;of &amp;nbsp;mutiple indexes)&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;My definition of Big Data is when any single table exceeds 1TB, Big Computation is when more than 16 cores are needed.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;WPS, the SAS clone, does not restrict workstation cores or memory?&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Servers for petabytes and large simulations.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Mon, 12 Dec 2016 14:08:09 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318274#M9142</guid>
      <dc:creator>rogerjdeangelis</dc:creator>
      <dc:date>2016-12-12T14:08:09Z</dc:date>
    </item>
    <item>
      <title>Re: Performance, joining two tables 14m rows</title>
      <link>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318280#M9143</link>
      <description>Disregard the the comment about the second count distinct. My fingers were ahead of my brain. The grouping variable only works if the grouping variable is somehow part of the variable you want a count distinct of.&lt;BR /&gt;&lt;BR /&gt;ie 'NY-20906-PLAN-163'&lt;BR /&gt;'VT-05301-PLAN-188'&lt;BR /&gt;&lt;BR /&gt;In this case you can count distinct states&lt;BR /&gt;and then sum&lt;BR /&gt;&lt;BR /&gt;This</description>
      <pubDate>Mon, 12 Dec 2016 14:24:39 GMT</pubDate>
      <guid>https://communities.sas.com/t5/SAS-Data-Management/Performance-joining-two-tables-14m-rows/m-p/318280#M9143</guid>
      <dc:creator>rogerjdeangelis</dc:creator>
      <dc:date>2016-12-12T14:24:39Z</dc:date>
    </item>
  </channel>
</rss>

