SQL Query(ies) to Gather Data for Common Business Reports - sql

I have a database with a 'General Ledger', 'Account Types' and 'Chart of Accounts' tables. The layout is as follows:
'Account Types':
AcctTypeID Autonumber Long Integer Not Null Primary Key
TypeName Text(50) Not Null
Description Memo
Credit Yes/No Not Null Default Yes
'Chart of Accounts':
AcctID Long Integer Not Null Primary Key
AcctName Text(30) Not Null
Description Memo
AcctType Long Integer Not Null
'General Ledger':
EntryID Autonumber Long Integer Not Null Primary Key
EntryDate Date/Time Not Null Default #1/1/1900#
EntryType Text(12) Not Null Default "DC"
Description Memo
FromAcct Long Integer Not Null
ToAcct Long Integer Not Null
Taxable Yes/No Not Null Default No
Relationships Between Tables:
One-to-Many from 'Chart of Accounts'->AcctID to 'General Ledger'->FromAcct
One-to-Many from 'Chart of Accounts'->AcctID to 'General Ledger'->ToAcct
One-to-Many from 'Account Types'->TypeID to 'Chart of Accounts'->AcctType
I'm needing to start by fulfilling the following requirements in two different SQL queries:
If ToAcct.AcctType == (Income OR Asset) then add 'General Ledger'.Amount to a TotalRevenue
If ToAcct.AcctType == (Expense OR Liability) then add 'General Ledger'.Amount to TotalExpenses
I've tried these two SQL queries, before I modified my 'Account Types' to include whether they are income/assets or expenses/liabilities:
SELECT SUM(IIF((Credit = TRUE), Amount, 0)) AS RevenueTotal FROM GeneralLedger;
SELECT SUM(IIF((Credit = FALSE), Amount, 0)) AS ExpenseTotal FROM GeneralLedger
These queries worked, but I had the extra checkbox on the General Ledger form for "Credit". In normalizing, I figured it would be better for the account to determine whether it is a debit or a credit account.
After multiple searches on Google and reading I-don't-know-how-many questions and answers, I'm not seeing anything that is similar to what I'm trying to do here.
The output should give me the information needed for creating the Profit & Loss, Income Statement, Business and Tax reports. I believe I can get the SQL queries going for pulling the amounts based on their account, but I'm not sure how to get at the account types' 'Debit' field to determine whether to add or subtract the amount.
If necessary, I may do this inside the C# application that this database will underpin. If I can do it at the SQL Query level, I'd be able to use it in the project as a view, instead of having to code it. Any help at all is greatly appreciated.

After a couple more searches, I found my answer as being that I need to do 4 different queries to accomplish what I was attempting. The appropriate queries turned out to be:
For Total Expenses:
SELECT SUM(Amount) AS TotalExpense
FROM GeneralLedger INNER JOIN ChartOfAccounts ON ChartOfAccounts.AcctID=GeneralLedger.ToAcct
WHERE ChartOfAccounts.AcctType = 4;
For Total Revenue:
SELECT SUM(Amount) AS TotalRevenue
FROM GeneralLedger INNER JOIN ChartOfAccounts ON ChartOfAccounts.AcctID=GeneralLedger.ToAcct
WHERE ChartOfAccounts.AcctType = 3;
For Total Liabilities:
SELECT SUM(Amount) AS TotalLiabilities
FROM GeneralLedger INNER JOIN ChartOfAccounts ON ChartOfAccounts.AcctID = GeneralLedger.ToAcct
WHERE ChartOfAccounts.AcctType = 2;
For Total Assets:
SELECT SUM(Amount) AS TotalAssets
FROM GeneralLedger INNER JOIN ChartOfAccounts ON ChartOfAccounts.AcctID = GeneralLedger.ToAcct
WHERE ChartOfAccounts.AcctType = 1;
It was just a matter of getting my INNER JOIN working with my WHERE clause properly. It's the simple things that kill...
The link that proved most helpful was: StackOverflow: sql - INNER JOIN WHERE clause.

Related

DB design for Global and Specific

I need some help, inputs on DB design practices to achieve the below requirement. The database will be PostgreSQL.
I have to design a holiday tracker with the below requirements.
Global Holiday for the whole country.
Holiday per state.
Ex :
December 25th: Holiday to the whole country. (All)
Let's Say, January 25th: Holiday for "MO, IL" states.
So when query holiday for state MO or IL it should return both December 25th and January 25th.
But when a query for other states (ex: TN) it should only return December 25th only.
Tried DB design.
With this design for find global holidays, I have use OR Condition.
Ex;
select * from holiday_master hm
where exists ( select 'x' from state_holiday sh
where sh.holiday_id = hm.id
and state_id in ("MO", "IL") )
or not exists ( select 'x' from state_holiday sh
where sh.holiday_id = hm.id )
One option is to add an entry for state_holiday with state id as All and change query as below.
select * from holiday_master hm
where exists ( select 'x' from state_holiday sh
where sh.holiday_id = hm.id
and state_id in ("MO", "IL", "All") )
Please provide your great inputs here.
Note: No of States can be increased from 1 to 1000.
I would normally steer clear of any solution that involved a deliberate cartesian join, but based on the fact that we are dealing with very low data volumes (states < 1,000 records, holidays <= 365 records), you could make use of Postgres' most excellent array capabilities.
create table holiday_master (
id integer not null,
holiday_date date not null,
description text,
national_holiday boolean not null,
states integer[],
constraint holiday_master_pk primary key (id)
)
And then a sample query that should yield all holidays for a given state would look like:
select
s.state, h.holiday_date, h.description
from
state_master s
cross join holiday_master h
where
s.state = 'MO' and
(h.national_holiday or s.id = any (h.states))

Compare data in the same table

I have a table that stores monthly data and I would like to create a comparison between the quantity movement within a period.
Here is an example of the table
.
The SQL statement below is meant to return any changes that has happened within a period - any fund/policy partial or total loss as well as partial or total gain. I have been battling with it for a while - any help would be well appreciated.
I currently have 5 sets of unions - (where the policies and funds match and there's a difference in quantities held, where the policies exist in the previous and not in the current and vice versa and where the securities exist in the previous and not in the current and vice versa) but the other unions work save for the last couple (where the securities exist in the previous and not in the current and vice versa). It doesn't seem to return every occurrence.
SELECT distinct pc.[Client]
,pc.Policy
,cast(pc.Qty as decimal) AS CurrQ
,0 AS PrevQ
,cast(pc.Qty as decimal) - 0 AS QtyDiff
,CASE WHEN cast(pc.Qty as decimal) - 0 > 0 THEN 'Bought Units'
WHEN cast(pc.Qty as decimal) - 0 < 0 THEN 'Sold Units'
ELSE 'Unknown'
END AS TransactionType
,convert(varchar,cast(pc.[ValDate] as date),103) AS CurrValDate
,'' AS PrevValDate
FROM table pc
WHERE convert(varchar,cast(pc.[ValDate] as date),103) = convert(varchar,getdate(),103)
AND pc.Policy IN (SELECT policy
FROM table
WHERE convert(varchar(10),[ValDate],103) = convert(varchar(10),getdate()-1,103)
AND pc.[Fund] NOT IN (SELECT PM.[Fund]
FROM table pc
LEFT JOIN table pm ON pc.policy = pm.policy
WHERE convert(varchar,cast(pc.[ValDate] as date),103) = convert(varchar,getdate(),103))
AND convert(varchar,cast(pm.[ValDate] as date),103) = convert(varchar,getdate()-1,103))
As #Larnu rightly mentioned in the comment section, the extra conditions in the query changed the run from a LEFT JOIN to an INNER JOIN. I changed the code to have policy, fund and date in the ON clause:
FROM table pc
LEFT JOIN table pm ON (pc.policy = pm.policy
AND pc.fund = pm.fund
AND pc.[ValDate]-1 = pm.[ValDate])
and got rid of the sub queries.
Thanks again Larnu.

SQL-Server drill down in an other table (group by)

OK, I'm quite new to SQL and didn't get to much training!
I'm using SSMS to create stored procedures and open them in Excel.
The code below work just fine, but I need to add a drill down to get more info on some lines.
We need to follow what was invoice and paid on a batch of contracts for our project. Each contract have multiple lines with a description and a couple of other fields(reference, line #, G/L # etc). Plus we have the value of the line, the amount that was invoice for this line and the amount that was paid.
Main table 'CSCOMVTL' have the basic infos including the base value and the invoice amount, but not the paid amount.
'JRAPRVTL' is the list of all invoices with; invoice no., invoice date, invoiced amount and paid amount that we may need to see.
So for each base line, we need a +/- button to show/hide details of the invoice.
Invoice amount and paid amount could be from a rollup, but the number and date won't be on the parent line. If they could be in the same column as other field not needed it would be great, but I could live with 2 extra columns.
Thanks!
ALTER PROCEDURE [dbo].[marpt_qmd_AccPmt_DetailsST]
#contrat varchar(30), #projet varchar(30)
AS
BEGIN
CREATE TABLE #RPT
(
Ligne INT,
Lien INT,
Act VARCHAR (10),
Descr VARCHAR (90),
MntBase DECIMAL (20,2),
MntFact DECIMAL (20,2),
Modif VARCHAR (40),
Descr3 VARCHAR (90),
Lien2 INT,
MntPy DECIMAL (20,2) default '0',
)
INSERT INTO #RPT (Ligne, Lien, Act, Descr, MntBase, MntFact)
SELECT ROW, DETAILCHANGEORDERCOU, ACTIVITY, DESCRIPTION, AMOUNT, INVOICE
FROM cscomvtl
WHERE PROJECTNUMBER = #projet
and LTRIM(RTRIM(PONUMBER)) = #contrat
UPDATE #RPT
SET Modif=m.CHANGEORDERNUMBER, Descr3=m.DESCRIPTION, Lien2=m.CHANGEORDERCOUNTER
FROM cscomac m, #RPT r
where m.COUNTER=r.Lien
UPDATE #RPT
SET MntPy=payment
FROM #RPT r, (select POLINE, sum(payment) payment from jraprvtl where PROJECTNO=#projet
and LTRIM(RTRIM(PURCHASEORDER))=#contrat group by POLINE) d
where r.Ligne=d. POLINE
SELECT
Ligne as 'Ligne',
Act as 'Act.',
Descr as 'Description 1',
MntBase as '$ Base',
MntFact as '$ Invoiced',
Modif as 'Num. Modif.',
Descr3 as 'Description 2',
MntPy as '$ Paid'
FROM #RPT
Order by Ligne
Drop table #RPT
First off, take the time & learn SQL. It's an invaluable tool in your toolkit!
Okay, enough of the lecture. In looking through your code, you don't seem to really need the temp table #rpt, you just need to understand JOINs. Hopefully this SQL will get you what you are looking for:
SELECT vtl.ROW AS Ligne, vtl.DETAILCHANGEORDERCOU AS Lein, vtl.ACTIVITY AS Act,
vtl.DESCRIPTION AS Descr, vtl.AMOUNT AS MntBase, vtl.INVOICE AS MntFact,
mac.CHANGEORDERNUMBER AS Modif, mac.DESCRIPTION AS Descr3, mac.CHANGEORDERCOUNTER AS Lien2,
sum(jrap.payment) AS MntPy
FROM cscomvtl AS vtl
LEFT OUTER JOIN cscomac AS mac
ON vtl.detailchangeordercou = mac.counter
LEFT OUTER JOIN jraprvtl AS jrap
ON vtl.row = jrap.poline
WHERE projectnumber = #projet AND LTRIM(RTRIM(ponumber)) = #contrat
GROUP BY vtl.row, vtl.detailchangeordercou, vtl.activity, vtl.description, vtl.amount,
vtl.invoice, mac.changeordernumber, mac.description, mac.changeordercounter
You will likely have to tweak it to fit what you're trying to do in Excel, since you really didn't give much to go on there.

Postgresql query for every day sold stock count

I have project on CRM which maintains product sales order for every organization.
I want to count everyday sold stock which I have managed to do by looping over by date but obviously it is a ridiculous method and taking more time and memory.
Please help me to find out it in single query. Is it possible?
Here is my database structure for your reference.
product : id (PK), name
organization : id (PK), name
sales_order : id (PK), product_id (FK), organization_id (FK), sold_stock, sold_date(epoch time)
Expected Output for selected month :
organization | product | day1_sold_stock | day2_sold_stock | ..... | day30_sold_stock
http://sqlfiddle.com/#!15/e1dc3/3
Create tablfunc :
CREATE EXTENSION IF NOT EXISTS tablefunc;
Query :
select "proId" as ProductId ,product_name as ProductName,organizationName as OrganizationName,
coalesce( "1-day",0) as "1-day" ,coalesce( "2-day",0) as "2-day" ,coalesce( "3-day",0) as "3-day" ,
coalesce( "4-day",0) as "4-day" ,coalesce( "5-day",0) as "5-day" ,coalesce( "6-day",0) as "6-day" ,
coalesce( "7-day",0) as "7-day" ,coalesce( "8-day",0) as "8-day" ,coalesce( "9-day",0) as "9-day" ,
coalesce("10-day",0) as "10-day" ,coalesce("11-day",0) as "11-day" ,coalesce("12-day",0) as "12-day" ,
coalesce("13-day",0) as "13-day" ,coalesce("14-day",0) as "14-day" ,coalesce("15-day",0) as"15-day" ,
coalesce("16-day",0) as "16-day" ,coalesce("17-day",0) as "17-day" ,coalesce("18-day",0) as "18-day" ,
coalesce("19-day",0) as "19-day" ,coalesce("20-day",0) as "20-day" ,coalesce("21-day",0) as"21-day" ,
coalesce("22-day",0) as "22-day" ,coalesce("23-day",0) as "23-day" ,coalesce("24-day",0) as "24-day" ,
coalesce("25-day",0) as "25-day" ,coalesce("26-day",0) as "26-day" ,coalesce("27-day",0) as"27-day" ,
coalesce("28-day",0) as "28-day" ,coalesce("29-day",0) as "29-day" ,coalesce("30-day",0) as "30-day" ,
coalesce("31-day",0) as"31-day"
from crosstab(
'select hist.product_id,pr.name,o.name,EXTRACT(day FROM TO_TIMESTAMP(hist.sold_date/1000)),sum(sold_stock)
from sales_order hist
left join product pr on pr.id = hist.product_id
left join organization o on o.id = hist.organization_id
where EXTRACT(MONTH FROM TO_TIMESTAMP(hist.sold_date/1000)) =5
and EXTRACT(YEAR FROM TO_TIMESTAMP(hist.sold_date/1000)) = 2017
group by hist.product_id,pr.name,EXTRACT(day FROM TO_TIMESTAMP(hist.sold_date/1000)),o.name
order by o.name,pr.name',
'select d from generate_series(1,31) d')
as ("proId" int ,product_name text,organizationName text,
"1-day" float,"2-day" float,"3-day" float,"4-day" float,"5-day" float,"6-day" float
,"7-day" float,"8-day" float,"9-day" float,"10-day" float,"11-day" float,"12-day" float,"13-day" float,"14-day" float,"15-day" float,"16-day" float,"17-day" float
,"18-day" float,"19-day" float,"20-day" float,"21-day" float,"22-day" float,"23-day" float,"24-day" float,"25-day" float,"26-day" float,"27-day" float,"28-day" float,
"29-day" float,"30-day" float,"31-day" float);
Please note, use PostgreSQL Crosstab Query. I have used coalesce for handling null values(Crosstab Query to show "0" when there is null data to return).
Following query will help to find the same:
select o.name,
p.name,
sum(case when extract (day from to_timestamp(sold_date))=1 then sold_stock else 0 end)day1_sold_stock,
sum(case when extract (day from to_timestamp(sold_date))=2 then sold_stock else 0 end)day2_sold_stock,
sum(case when extract (day from to_timestamp(sold_date))=3 then sold_stock else 0 end)day3_sold_stock,
from sales_order so,
organization o,
product p
where so.organization_id=o.id
and so.product_id=p.id
group by o.name,
p.name;
I just provided logic to find for 3 days, you can implement the same for rest of the days.
basically first do basic joins on id, and then check if each date(after converting epoch to timestamp and then extract day).
You have a few options here but it is important to understand the limitations first.
The big limitation is that the planner needs to know the record size before the planning stage, so this has to be explicitly defined, not dynamically defined. There are various ways of getting around this. At the end of the day, you are probably going to have somethign like Bavesh's answer, but there are some tools that may help.
Secondly, you may want to aggregate by date in a simple query joining the three tables and then pivot.
For the second approach, you could:
You could do a simple query and then pull the data into Excel or similar and create a pivot table there. This is probably the easiest solution.
You could use the tablefunc extension to create the crosstab for you.
Then we get to the first problem which is that if you are always doing 30 days, then it is easy if tedious. But if you want to do every day for a month, you run into the row length problem. Here what you can do is create a dynamic query in a function (pl/pgsql) and return a refcursor. In this case the actual planning takes place in the function and the planner doesn't need to worry about it on the outer level. Then you call FETCH on the output.

SQL Query: joining three tables to generate a Sales Report of Spend

I normally work with MySQL so not sure how to get a query to work on SQL.
I have 3 tables:
Table1
ODBC_ORDER_LINE_ALL
This contains ORDER_LINE_NETT_TOTAL and also the ORDER_LINE_ORDER_ID
Table 2
ODBC_ORDER_ALL
This contains the ORDER_ID and also the ORDER_ACCOUNT_ID
Table 3
ODBC_ACCOUNT
This contains the ACCOUNT_ACCID and some other information on the account we may need in the future, hence why we are adding it.
I am joining table 1,2,3 together using the above constraints.
What I want to be able to do is generate a Sales Report of Spend showing Account Number and Total Spend but I can't seem to get the syntax correct.
Any help much appreciated.
Here are the tables:
ODBC_ORDER_LINE_ALL
ORDER_LINE_ID
ORDER_LINE_ORDER_ID
ORDER_LINE_DATE
ORDER_LINE_MEDIACODE
ORDER_LINE_SOURCE
ORDER_LINE_PRODUCT_ID
ORDER_LINE_PRODUCT_CODE
ORDER_LINE_PRODUCT_NAME
ORDER_LINE_QUANTITY
ORDER_LINE_NETT_PRICE
ORDER_LINE_VAT_CODE
ORDER_LINE_VAT_RATE
ORDER_LINE_VAT
ORDER_LINE_NETT_TOTAL
ORDER_LINE_VAT_TOTAL
ORDER_LINE_GROSS_TOTAL
ORDER_LINE_UNIT_COST
ORDER_LINE_COST_CURRENCY
ORDER_LINE_COST_EXCHANGE_RATE
ORDER_LINE_TYPE
ORDER_LINE_STAGE
ORDER_LINE_STAGE_DATE
ORDER_LINE_INVOICE_ID
ORDER_LINE_INVOICE_DATE
ORDER_LINE_KIT_COMPONENT
ORDER_LINE_KIT_LINE_ID
ORDER_LINE_USER
ODBC_ORDER_ALL
ORDER_ID
ORDER_TYPE
ORDER_MEDIA_CODE_ID
ORDER_MEDIA_CODE
ORDER_MEDIA_SUBCODE
ORDER_TAKEN_BY
ORDER_DATE
ORDER_ACCOUNT_ID
ORDER_DELIVERY_ACCOUNT_ID
ORDER_SOURCE_ID
ORDER_SOURCE
ORDER_WEB_ORDER_ID
ORDER_MULTI_CLIENT_ID
ORDER_MULTI_CLIENT
ODBC_ACCOUNT
ACCOUNT_ACCID
ACCOUNT_ACC_TYPE
ACCOUNT_REF
ACCOUNT_TITLE
ACCOUNT_FORENAME
ACCOUNT_SURNAME
ACCOUNT_COMPANY_NAME
ACCOUNT_HOUSE_NAME
ACCOUNT_ADD1
ACCOUNT_ADD2
ACCOUNT_ADD3
ACCOUNT_ADD4
ACCOUNT_POSTCODE
ACCOUNT_COUNTRY
ACCOUNT_PHONE1
ACCOUNT_PHONE2
ACCOUNT_FAX1
ACCOUNT_FAX2
ACCOUNT_EMAIL
ACCOUNT_WEBSITE
ACCOUNT_PRICELIST_ID
ACCOUNT_MEDIACAMPAIGN_ID
ACCOUNT_CREATED_DATE
Assuming that the table names are 'Table 1' ODBC_Order_Line_All, 'Table 2' ODBC_Order_All, 'Table 3' ODBC_Account, then you simply need to join the three tables on the relevant joining columns, and sum the order line nett total entries for each account:
SELECT A.Account_AccID, SUM(L.Order_Line_Nett_Total) AS TotalSpend
FROM ODBC_Account AS A
JOIN ODBC_Order_All AS O ON A.Account_Acc_ID = O.Order_Account_ID
JOIN ODBC_Order_Line_All AS L ON O.Order_ID = L.Order_Line_Order_ID
GROUP BY A.Account_AccID;
However, while there is nothing but the Account_AccID selected from ODBC_Account, there is no actual need to select from the ODBC_Account table; you can join just the two tables, yielding a simpler query:
SELECT O.Order_Account_ID AS Acount_AccID, SUM(L.Order_Line_Nett_Total) AS TotalSpend
FROM ODBC_Order_All AS O ON A.Account_Acc_ID = O.Order_Account_ID
JOIN ODBC_Order_Line_All AS L ON O.Order_ID = L.Order_Line_Order_ID
GROUP BY Account_AccID;
There is nothing here that is different in MySQL from any other SQL DBMS, so your knowledge of MySQL should transfer directly.