SQL Basics for Dynamics GP
This article is my little contribution in getting you started
with SQL basics. I’m sure there are a ton of articles about SQL out there and
some are even related to Dynamics GP. Hopefully this will add a little something
to them.
Opening up a SQL Query window:
In order to write and submit queries to SQL Server, you must
have access to a SQL Query window. The SQL Query tools that come with Microsoft
SQL Server is the most common way to get access and this is what will be covered
in this article.
If you’re on SQL 2005 or greater, you will need to use the
SQL Management Console tool. You can find it via Start -> Programs -> Microsoft
SQL Server 2005 -> SQL Server Management Studio. Connect using the credentials
provided to you by the DBA. Click on the ‘New Query’ button in the toolbar. SQL
2000 isn’t much different but in the interest of brevity, I’ll leave that to
older articles that may be out on the web to cover that.
Viewing the databases:
Once you’re connected, you’ll see a window to your left
called the Object Explorer. It basically lists a tree of SQL servers, databases,
etc. Find the server that Dynamics GP is installed on and click on the (+) to
the left of it to expand the contents. Once expanded, you’ll see the first entry
called ‘Databases’. Expand that and you’ll see a list of the databases on the
server.
At minimum, you should see 2 databases that were created by
the Dynamics GP installation process. These are DYNAMICS, and TWO.
DYNAMICS is the system level database that contains the setup
information that is common to all companies in Dynamics GP. Examples of these
include security information, tax codes, users, etc.
TWO is the database that holds all of the data for the
Fabrikam sample company. Incidentally, for those that are curious, TWO actually
stood for ‘The World Online’ in the past. Microsoft renamed the sample company
to ‘Fabrikam’ but left the database as ‘TWO’.
If you have additional databases (not including the SQL
system databases), the odds are they are other companies that were set up in
Dynamics GP. However, don’t take this for granted as these could also be
databases that were set up for other systems.
Viewing the tables:
To view the tables, expand one of the company databases (i.e.
TWO). Then expand the ‘tables’ folder. You should see a huge list of virtually
meaningless table names (they actually have some meaning – I’ll explain in
another article).
Feel free to explore around these SQL objects. Just don’t
press anything like ‘delete’, etc. That wouldn’t be good.
Writing SQL queries:
Well, now that you know how to open up a query window and
navigate around SQL objects, it’s time to go over basic SQL queries.
There are really 4 things in SQL that are at the core of what
SQL is about. These are:
1.
Creating records in tables (INSERT).
2.
Reading records from tables (SELECT).
3.
Updating records in tables (UPDATE).
4.
Deleting records from tables (DELETE).
If you notice, the acronym spells CRUD. This is actually a
commonly used acronym in database development to refer to the basic operations
to data in a database. So, when someone is talking about CRUD, they’re not
trying to be vulgar.
SELECT:
The most common task you’ll probably find yourself doing is
performing a SELECT on a table. This should be the most common task because
before you do anything to a record, you need to ensure that you’re doing it to
the correct one. So, here’s how you would read a record from a table:
select
*
from
PM00200
·
The ‘select’ keyword tells SQL that you want to read a set of
records.
·
The ‘*’ tells SQL that you want to get all the columns in the
table.
·
The ‘from’ keyword tells SQL that the next statement will be a
table from which you wish to retrieve records from.
·
Finally, ‘PM00200’ is the table name from which you wish to read
records. ‘PM00200’ is actually the Vendor Master table in Dynamics GP.
Since we didn’t specify which records to retrieve, this
statement will display all the records in the table.
If you wish to see only specific columns for a range of
customers, here’s what you’d do:
select
VendorNo = VENDORID,
VendorName = VENDNAME
from
PM00200
where
VENDORID like 'ACE%'
·
‘VendorNo’ is just the column name that I would like to appear in
the query. It doesn’t exist anywhere in the database. I’m basically saying: give
me the column VENDORID but call it ‘VendorNo’ when you return the results.
·
The ‘where’ clause tells SQL that the next phrase will be a filter
of the data. It will essentially return those records specified in the select
statement only when everything in the ‘where’ clause is true.
·
The last line ‘VENDORID like ‘ACE%’’ says that SQL should return
all records in the PM00200 table where the vendor number (VENDORID) starts with
‘ACE’.
If you run this in the TWO database, you should get one
record as follows:
VendorNo VendorName
---------------
-----------------------------------------------------------------
ACETRAVE0001 A Travel Company
(1 row(s) affected)
What about those cases where you need to get data from more
than one table at a time. Essentially, you want to JOIN two tables. Well, that’s
exactly what we’re going to do. Take a look at this:
select
VendorNo = a.VENDORID,
VendorName = a.VENDNAME,
VendorBalance = b.CURRBLNC
from
PM00200 a
join PM00201 b on a.VENDORID = b.VENDORID
where
a.VENDORID
like 'ACE%'
- You’ll notice in the ‘from’ clause, we’re joining PM00200
to PM00201. We’re telling SQL that we want to read records from both of
these tables. In this case, we’re saying: give me the records from
PM00200 and PM00201 where the value for the ‘VENDORID’ columns in each
table is the same.
- Also notice the ‘a’ and ‘b’. These are actually ‘aliases’.
It basically tells SQL that I’m too lazy to spell out the whole table name
and I just want to use a short cut. Some people use something a little
more descriptive, but I tend to prefer just using ‘a, b, c …’.
- You should also note that when you refer to more than one
table in a query, SQL can get confused when both tables have the same
column names. When this happens, you’ll need to prefix the column name
with either the table name or the alias. As shown in the above query,
notice we have both ‘a.VENDORID’ and ‘b.VENDORID’. If we had just left it
as ‘VENDORID’, SQL would have complained with an: ‘Ambiguous column name 'VENDORID'.’
Here’s the result of your effort:
VendorNo VendorName
VendorBalance
---------------
-----------------------------------------------------------------
---------------------------------------
ACETRAVE0001 A Travel
Company 6713.27000
(1 row(s) affected)
UPDATE:
This next section will describe how to update records in a
table.
Let’s say that you negotiated a better payment term with all
your vendors that are in the ‘USA-US-M’ vendor class. Normally, you would just
specify the payment term in a vendor class and roll it down using the Dynamics
GP user interface, but in this case, you’re feeling a little adventurous and
want to try it through the back-end (please make sure you know what you’re
doing). Let’s say you want to change these vendors’ payment terms from ‘Net 30’
to ‘2% 10/Net 30’. Here’s what you’d do:
update
PM00200
set
PYMTRMID = '2% 10/Net 30'
from
PM00200
where
VNDCLSID = 'USA-US-M' and
PYMTRMID = 'Net 30'
Please note that these payment terms only exist in the TWO
database. They may not actually exist in your own company database and so you
would have to find out the correct payment terms to replace the ones I’m playing
with here.
- The ‘update’ clause is telling SQL that I want to update
the PM00200 table.
- The ‘set’ clause is saying that I want to update the
column value for PYMTRMID to the constant ‘2% 10/Net 30’. You can also
refer to another column name.
- Everything else in the statement is just like I described
for the ‘SELECT’ section above. In this case, I’m telling SQL that I want
to update the PM00200 table for all records where the VNDCLSID (Vendor
Class ID) is ‘USA-US-M’ and the PYMTRMID (Payment Terms ID) is ‘Net 30’.
If you want to be prudent and verify what you’re about to
change, do a SELECT statement like this:
select
PYMTRMID = '2% 10/Net 30'
from
PM00200
where
VNDCLSID = 'USA-US-M' and
PYMTRMID = 'Net 30'
You’ll notice that I just copied everything after the ‘set’
in the update statement and pasted it after a ‘select’. You may wish to add a
column to show you the Vendor ID and perhaps the previous value of PYMTRMID just
to make sure. I.e.:
select
VendorNo = VENDORID,
PYMTRMID = '2% 10/Net 30',
OldPaymentTermID = PYMTRMID
from
PM00200
where
VNDCLSID = 'USA-US-M' and
PYMTRMID = 'Net 30'
You might get something like this. So, if you ran the UPDATE
query above, you would have updated 25 records that are indicated below:
VendorNo PYMTRMID OldPaymentTermID
--------------- ------------ ---------------------
ADVANCED0001 2% 10/Net 30 Net 30
ALLENSON0001 2% 10/Net 30 Net 30
AMERICAN0001 2% 10/Net 30 Net 30
ASSOCIAT0001 2% 10/Net 30 Net 30
BEAUMONT0001 2% 10/Net 30 Net 30
BLOOMING0001 2% 10/Net 30 Net 30
CHICAGOC0001 2% 10/Net 30 Net 30
CHICAGOR0001 2% 10/Net 30 Net 30
DOLECKIC0001 2% 10/Net 30 Net 30
TIMELYSH0001 2% 10/Net 30 Net 30
WOODGROV0001 2% 10/Net 30 Net 30
GKCLEANI0001 2% 10/Net 30 Net 30
AUTOFINA0001 2% 10/Net 30 Net 30
HARVEYEL0001 2% 10/Net 30 Net 30
ILSTATE0001 2% 10/Net 30 Net 30
IMAGEMAK0001 2% 10/Net 30 Net 30
INSTATET0001 2% 10/Net 30 Net 30
INTERNAL0001 2% 10/Net 30 Net 30
KNOPFLER0001 2% 10/Net 30 Net 30
MIDWESTA0001 2% 10/Net 30 Net 30
MIDWESTT0001 2% 10/Net 30 Net 30
OFFICESP0001 2% 10/Net 30 Net 30
PROFESSI0001 2% 10/Net 30 Net 30
SHIPPING0001 2% 10/Net 30 Net 30
WESTAMER0001 2% 10/Net 30 Net 30
(25 row(s) affected)
As always, be careful. As my father always said, measure
twice, cut once. If you accidentally update the wrong set of records, the only
way to recover is to restore a backup or use your new found SQL wisdom to update
those records back to what they were. Not always an easy task.
What about those cases where you want to update records in
one table with values from another table? As an example, we have a Tax Code in
the Vendor Address table as well as the Vendor Master table. We want to make
sure that all the Tax Codes in the Vendor Address table match the Tax Codes in
the Vendor Master table for the same vendor (no matter what the address is). So,
we want to update the Tax Schedule ID (TAXSCHID) in the PM00300 (Vendor Address)
table from values we get in the PM00200 (Vendor Master) table.
Here’s how we’d do it:
update
a
set
TAXSCHID = b.TAXSCHID
from
PM00300 a
join
PM00200 b on a.VENDORID
= b.VENDORID
- You’ll notice that I’m now doing an ‘update’ on ‘a’. This
is actually an alias. In this case, ‘a’ is equivalent to ‘PM00300’.
- In the ‘set’ section, I’m telling SQL that I want to
update the TAXSCHID field (in the PM00300 table) to ‘b.TAXSCHID’. Since
‘b’ is an alias referring to the PM00200 table, I’m essentially telling
SQL that I want to set it to PM00200.TAXSCHID.
- The ‘from’ clause works just like I described in the SELECT
portion above. In this case, I’m telling SQL that I want to update all
those records that have the same vendor id in both PM00300 and PM00200.
I’m also telling SQL that I want to update the TAXSCHID to the value in
TAXSCHID from the PM00200 table for the same vendor.
To see what you’re actually updating before you update it,
write a SELECT statement:
select
VendorNo = a.VENDORID,
AddressCode = a.ADRSCODE,
OldTaxScheduleID = a.TAXSCHID,
TAXSCHID = b.TAXSCHID
from
PM00300 a
join PM00200 b on a.VENDORID = b.VENDORID
INSERT:
Well, I showed you how to SELECT on records and UPDATE them.
Now I’ll show you how to INSERT records in to a table.
Let’s say that a gremlin showed up in Dynamics GP and decided
to remove a record mysteriously from the PM00400 (PM Keys) table. You could run
Check-links but in a company with a lot of data, that may take a very long time.
So, you decide you’d like to insert back that record. Here’s how you’d go about
doing that.
The first thing you should do is determine what sort of
fields you’ll need in your query. The best way to do that is ‘SELECT’ as always:
select
*
from
PM00400
What you see here should be enough to formulate a good INSERT
script:
insert
into
PM00400
(
CNTRLNUM,
CNTRLTYP,
DCSTATUS,
DOCTYPE,
VENDORID,
DOCNUMBR,
TRXSORCE,
CHEKBKID,
DUEDATE,
BCHSOURC,
DOCDATE,
USERID
)
select
CNTRLNUM = '00000000000000270',
CNTRLTYP = 1,
DCSTATUS = 3,
DOCTYPE = 6,
VENDORID = 'CONSUMER MAG',
DOCNUMBR = '20048',
TRXSORCE = 'PMCHK00000002',
CHEKBKID = 'UPTOWN TRUST',
DUEDATE = '01/01/1900',
BCHSOURC = 'XPM_Cchecks',
DOCDATE = '4/21/2017',
USERID = 'sa'
How I knew to insert those particle values is beyond the
scope of this article. For now, I’ll just say it’s experience. In future
articles, I might address how to dig for this sort of information. This should
be good enough to show you how you’d insert a record into a table.
- You’ll notice that this query is broken up into two parts
(the INSERT portion and the SELECT portion). If you just ran the SELECT
portion by itself, it will return a single record. The INSERT portion
must be run with the SELECT portion in order to work. You can also use
the VALUES key word instead of a SELECT statement (but you don’t really
need to ever use it – I never do). Look it up in SQL Books Online if you
want to learn how that works.
This particular query inserts a single record with each
column being a constant that I defined. You can also create a statement to
insert multiple records from a SELECT query. As an example, let’s say I want to
insert all those records into PM00400 that are missing. I can write something
like this:
insert
into
PM00400
(
CNTRLNUM,
CNTRLTYP,
DCSTATUS,
DOCTYPE,
VENDORID,
DOCNUMBR,
TRXSORCE,
CHEKBKID,
DUEDATE,
BCHSOURC,
DOCDATE,
USERID
)
select
CNTRLNUM = a.VCHRNMBR,
CNTRLTYP = 1,
DCSTATUS = 3,
DOCTYPE = a.DOCTYPE,
VENDORID = a.VENDORID,
DOCNUMBR = a.DOCNUMBR,
TRXSORCE = a.TRXSORCE,
CHEKBKID = a.CHEKBKID,
DUEDATE = a.DUEDATE,
BCHSOURC = a.BCHSOURC,
DOCDATE = a.DOCDATE,
USERID = 'sa'
from
PM30200 a
where
not exists (select 1
from PM00400 b
where a.VCHRNMBR = b.CNTRLNUM and
a.DOCTYPE = b.DOCTYPE)
and
a.DOCTYPE
= 6
- You’ll notice that the INSERT portion is just like the
previous example.
- The SELECT portion is just a typical query that you should
know how to write by now with the exception of the ‘not exists’ portion.
This query is essentially telling SQL that I want to retrieve all payment
records from the PM30200 (PM Paid Transaction History) table that do not
already exist in the PM00400 table. The ‘not exists’ portion of the query
is a fairly powerful technique but since I’m covering SQL basics in this
article, I’ll stop there. What you see in the paranthesis after the ‘not
exists’ is referred to as a ‘sub-query’.
DELETE:
Hopefully, you won’t have a lot of use for the DELETE
statement, but you may encounter it from time to time. For non-developers, the
most common reason to use DELETE is when perhaps a record is stuck in the batch
activity table (SY00800) or a user end-tasked their session and a record is
stuck in the user activity table (ACTIVITY). Both of these happen to be in the
DYNAMICS database.
Normally, if all users are logged out of Dynamics GP, you can
just delete all the records from the SY00800 table as follows in the DYNAMICS
database:
delete SY00800
If however, users are still in Dynamics GP, you’ll probably
need to put a where clause to only delete that record which happens to be stuck.
An example would be:
delete
SY00800
from
SY00800
where
BACHNUMB =
'?'
Replace the ‘?’ with whatever the Batch Number might be.
What if you want to get fancy with DELETEs? For example,
let’s say you want to delete all those Vendor Addresses that have the Address
Code ‘BILLING’ if there’s already an address code called ‘PRIMARY’. You can do
something like this:
delete
a
from
PM00300 a
join PM00300 b on a.VENDORID = b.VENDORID
where
a.ADRSCODE = 'BILLING' and
b.ADRSCODE
= 'PRIMARY'
- Notice that everything starting from the ‘from’ clause is
just like you’d see in a SELECT statement.
- What this statement is telling SQL is to delete all those
records in ‘a’ (same as PM00300 – the first one) where the two tables are
joined by the VENDORID and a.ADRSCODE is ‘BILLING’ and b.ADRSCODE is
‘PRIMARY’.
- Make sure you’re careful about which alias you use. For
example, if you accidentally used ‘b’, you would have deleted all the
‘PRIMARY’ records instead of the ‘BILLING’ records that we wanted to
remove.
Conclusion:
Everything I covered thus far is just the beginning. This
might be enough for you to do your basic functions as a functional consultant or
perhaps a Great Plains administrator, but if you really want to get an advanced
understanding of SQL coding, the best way is just read one of the countless
T-SQL books out there.
About the Author:
I’m Dan Sionov, founder of
accountingBot. I’ve been
developing software for Dynamics GP since 1995 and haven’t grown tired of it. We
make eftEmailer, a flexible yet simple to use application for Dynamics GP that
allows you to send e-mails to vendors receiving EFT direct deposits.