Rewrite MS Access SQL statement to SQL Server - sql

I wish to convert the following MS Access SQL statement to SQL Server. All of my attempts are resulting in different results from the old & original data.
SELECT
Sum(ADA_LAST.MA) AS MA,
Sum(ADA_LAST.DA) AS DA,
ADA_LAST.ID_BAS,
ADA_LAST.PRO_NUMBER,
ADA_LAST.ACC_NUMBER,
ADA_LAST.DATA,
"" AS Q,
"" AS P,
Last(ADA_LAST.Date) AS [DATE],
"" AS UNIT,
0 AS ID,
[MA]-[DA] AS R
FROM ADA_LAST
GROUP BY
ADA_LAST.ID_BAS,
ADA_LAST.PRO_NUMBER,
ADA_LAST.ACC_NUMBER,
ADA_LAST.DATA,
"",
0,
[MA]-[DA],
"",
""
;
The new Query is:
SELECT
MA = Sum([ADA_LAST].[MA]),
DA = Sum([ADA_LAST].[DA]),
[ADA_LAST].[ID_BAS],
[ADA_LAST].[PRO_NUMBER],
[ADA_LAST].[ACC_NUMBER],
[ADA_LAST].[DATA],
Q = '',
P = '',
[DATE] = ADA_LAST.[Date],
UNIT = '',
ID = 0,
Sum([ADA_LAST].[MA]) - Sum([ADA_LAST].[DA]) AS R
FROM [ADA_LAST](#PRO_NAME,#SDAY)
GROUP BY
[ADA_LAST].[ACC_NUMBER],
[ADA_LAST].[Date],
[ADA_LAST].[PRO_NUMBER],
[ADA_LAST].[ID_BAS],
[ADA_LAST].[DATA]
The problem caused by grouping date column in new statement, but in old one it is used in Last function to avoid grouping it and still exists in the select statement, How can I do like this.

You can try the below query. Changes to the original:
empty string is note as '' instead of ""
I replace LAST with MAX(); this is likely to do what you want, since you are using aggregation
constant columns do not need to be listed in the GROUP BY clause
Code:
SELECT
SUM(ADA_LAST.MA) AS MA,
SUM(ADA_LAST.DA) AS DA,
ADA_LAST.ID_BAS,
ADA_LAST.PRO_NUMBER,
ADA_LAST.ACC_NUMBER,
ADA_LAST.DATA,
'' AS Q,
'' AS P,
MAX(ADA_LAST.Date) AS [DATE],
'' AS UNIT,
0 AS ID,
[MA] - [DA] AS R
FROM ADA_LAST
GROUP BY
ADA_LAST.ID_BAS,
ADA_LAST.PRO_NUMBER,
ADA_LAST.ACC_NUMBER,
ADA_LAST.DATA,
[MA] - [DA]
;

Related

SQL 2-table query results need to be concatenated

My current SQL:
SELECT B.MESSAGENO, B.LINENO, B.LINEDATA
FROM BILL.MESSAGE AS B, BILL.ACTIVITYAS A
WHERE A.MSGNO = D.MESSAGENO AND A.FUPTEAM = 'DBWB'
AND A.ACTIVITY = 'STOPPAY' AND A.STATUS = 'WAIT'
AND A.COMPANY = D.COMPANY
MESSAGENO LINENO LINEDATA
1234567 1 CHEQUE NO : 9999999 RUN NO : 55555
1234567 2 DATE ISSUED: 12/25/2020 AMOUNT : 710.51
1234567 3 PAYEE : LASTNAME, FIRSTNAME
1234567 4 ACCOUNT NO : 12345-67890
1234567 5 USER : USERNAME
there are 550 sets of 5 lines per MESSAGENO
What I am trying to figure out is how I can get something like where LINENO = 1, concatenate LINEDATA so I just get 9999999 as checkno, where LINENO = 2, concatenate LINEDATA so I get 710.51 as amount, where LINENO = 3, concatenate LINEDATA so I get LASTNAME, FIRSTNAME as payee, where LINENO = 4, concatenate LINEDATA so I get LASTNAME, FIRSTNAME as payee, and lastly, the same thing for USERNAME.
I just cannot seems to conceptualize this. Every time I try, my brain starts turning into macaroni. Any help is appreciated.
UPDATED ANSWER, extracts all fields from stored strings:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=e22d26866053ea6088aa78dc23c4d809
Check this fiddle.
It uses a SUBSTRING_INDEX in the internal query to split the fields at the : or by a combination of : and " ". I used the two spaces because I wasn't sure what your actual whitespace was, and when I copied the data out of your post it was just spaces.
Then MAX is used in the outer query to get everything on one line, grouping by the messageNo. Since some lines have two pieces of data to extract, a second string parser was added. Here is the code from the fiddle, for reference. In this case, the table test was created from the output of OP's initial query, since I didn't have enough info to build both tables and completely recreate his output.
SELECT MESSAGENO,
MAX(if(LINENO = 1, extractFirst, null)) as checkNo,
MAX(if(LINENO = 1, extractLast, null)) as runNo,
MAX(if(LINENO = 2, extractFirst, null)) as issued,
MAX(if(LINENO = 2, extractLast, null)) as amount,
MAX(if(LINENO = 3, extractFirst, null)) as payee,
MAX(if(LINENO = 4, extractFirst, null)) as accountNo,
MAX(if(LINENO = 5, extractFirst, null)) as username
FROM (
SELECT MESSAGENO, LINENO,
trim(substring_index(substring_index(LINEDATA, ": ", -2), " ", 1)) as extractFirst,
trim(substring_index(LINEDATA, ":", -1)) as extractLast
FROM test
) z
GROUP BY MESSAGENO
Again, you will be much better off to alter your tables so that you can use simpler queries, as shown in the last fiddle.
===============================================
ORIGINAL ANSWER (demo of string parsing, suggestion for data model change)
You can achieve this with some string parsing BUT ABSOLUTELY DO NOT unless you have no choice. The reason you are having trouble is because your data shouldn't be stored this way.
I've included a fiddle incorporating this case statement and substring_index to extract the data. I have assumed mySQL 8 because you didn't specify SQL version.
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=068a49a2c819c08018691e54bcdf91e5
case when LINENO = 1 then trim(substring_index(substring_index(LINEDATA, "RUN NO", 1), ":", -1))
else trim(substring_index(LINEDATA, ":", -1)) end
as LDATA
See this fiddle for the full statement. I have just inserted the data from your join into a test table, instead of trying to recreate all your tables, since I don't have access to all the data I would need for that. In future, set up a fiddle like this one with some representative data and the SQL version, and it will be easier for people to help you.
=========================================
I think this is a better layout for you, with all data stored as the proper type and a field defined for each one and the extra text stripped out:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=11d52189b005740cdc53175466374635

Issue With SQL Pivot Function

I have a SQL query where I am trying to replace null results with zero. My code is producing an error
[1]: ORA-00923: FROM keyword not found where expected
I am using an Oracle Database.
Select service_sub_type_descr,
nvl('Single-occupancy',0) as 'Single-occupancy',
nvl('Multi-occupancy',0) as 'Multi-occupancy'
From
(select s.service_sub_type_descr as service_sub_type_descr, ch.claim_id,nvl(ci.item_paid_amt,0) as item_paid_amt
from table_1 ch, table_" ci, table_3 s, table_4 ppd
where ch.claim_id = ci.claim_id and ci.service_type_id = s.service_type_id
and ci.service_sub_type_id = s.service_sub_type_id and ch.policy_no = ppd.policy_no)
Pivot (
count(distinct claim_id), sum(item_paid_amt) as paid_amount For service_sub_type_descr IN ('Single-occupancy', 'Multi-occupancy')
)
This expression:
nvl('Single-occupancy',0) as 'Single-occupancy',
is using an Oracle bespoke function to say: If the value of the string Single-occupancy' is not null then return the number 0.
That logic doesn't really make sense. The string value is never null. And, the return value is sometimes a string and sometimes a number. This should generate a type-conversion error, because the first value cannot be converted to a number.
I think you intend:
coalesce("Single-occupancy", 0) as "Single-occupancy",
The double quotes are used to quote identifiers, so this refers to the column called Single-occupancy.
All that said, fix your data model. Don't have identifiers that need to be quoted. You might not have control in the source data but you definitely have control within your query:
coalesce("Single-occupancy", 0) as Single_occupancy,
EDIT:
Just write the query using conditional aggregation and proper JOINs:
select s.service_sub_type_descr, ch.claim_id,
sum(case when service_sub_type_descr = 'Single-occupancy' then item_paid_amt else 0 end) as single_occupancy,
sum(case when service_sub_type_descr = 'Multi-occupancy' then item_paid_amt else 0 end) as multi_occupancy
from table_1 ch join
table_" ci
on ch.claim_id = ci.claim_id join
table_3 s
on ci.service_type_id = s.service_type_id join
table_4 ppd
on ch.policy_no = ppd.policy_no
group by s.service_sub_type_descr, ch.claim_id;
Much simpler in my opinion.
for column aliases, you have to use double quotes !
don't use
as 'Single-occupancy'
but :
as "Single-occupancy",

How to use variables in SQL query when using Python and pyodbc

I am using Python to extract data from SQL by using ODBC to linking Python to SQL database. when I do the query, I need to use variables in the query to make my query result changeable. For example, my code is:
import pyodbc
myConnect = pyodbc.connect('DSN=B1P HANA;UID=***;PWD=***')
myCursor = myConnect.cursor()
Start = 20180501
End = 20180501
myOffice = pd.Series([1,2,3])
myRow = myCursor.execute("""
SELECT "CALDAY" AS "Date",
"/BIC/ZSALE_OFF" AS "Office"
FROM "SAPB1P"."/BIC/AZ_RT_A212"
WHERE "CALDAY" BETWEEN 20180501 AND 20180501
GROUP BY "CALDAY","/BIC/ZSALE_OFF"
""")
Result = myRow.fetchall()
d = pd.DataFrame(columns=['Date','Office'])
for i in Result:
d= d.append({'Date': i.Date,
'Office': i.Office},
ignore_index=True)
You can see that I retrieve data from SQL database and save it into a list (Result), then I convert this list to a data frame (d).
But, my problems are:
I need to specify a start date and an end data in myCursor.execute part, something like "CALDAY" BETWEEN Start AND End
Let's say I have 100 offices in my data. Now I just need 3 of them (myOffice). So, I need to put a condition in myCursor.execute part, like myOffice in (1,2,3)
In R, I know how to deal with these two problems. the code is like:
office_clause = ""
if (myOffice != 0) {
office_clause = paste(
'AND "/BIC/ZSALE_OFF" IN (',paste(myOffice, collapse=", "),')'
)
}
a <- sqlQuery(ch,paste(' SELECT ***
FROM ***
WHERE "CALDAY" BETWEEN',Start,'AND',End,'
',office_clause1,'
GROUP BY ***
'))
But I do not know how to do this in Python. How can I do this?
You can use string formatting operations for this.
First define
query = """
SELECT
"CALDAY" AS "Date",
"/BIC/ZSALE_OFF" AS "Office"
FROM
"SAPB1P"."/BIC/AZ_RT_A212"
WHERE
"CALDAY" BETWEEN {start} AND {end}
{other_conds}
GROUP BY
"CALDAY","/BIC/ZSALE_OFF"
"""
Now you can use
myRow = myCursor.execute(query.format(
start='20180501'
end='20180501',
other_conds=''))
and
myRow = myCursor.execute(query.format(
start='20180501'
end='20180501',
other_conds='AND myOffice IN (1,2,3)'))

how do I join two tables sql

I have an issue that I'm hoping you can help me with. I am trying to create charting data for performance of an application that I am working on. The first step for me to perform two select statements with my feature turned off and on.
SELECT onSet.testName,
avg(onSet.elapsed) as avgOn,
0 as avgOff
FROM Results onSet
WHERE onSet.pll = 'On'
GROUP BY onSet.testName
union
SELECT offSet1.testName,
0 as avgOn,
avg(offSet1.elapsed) as avgOff
FROM Results offSet1
WHERE offSet1.pll = 'Off'
GROUP BY offSet1.testName
This gives me data that looks like this:
Add,0,11.4160277777777778
Add,11.413625,0
Delete,0,4.5245277777777778
Delete,4.0039861111111111,0
Evidently union is not the correct feature. Since the data needs to look like:
Add,11.413625,11.4160277777777778
Delete,4.0039861111111111,4.5245277777777778
I've been trying to get inner joins to work but I can't get the syntax to work.
Removing the union and trying to put this statement after the select statements also doesn't work. I evidently have the wrong syntax.
inner join xxx ON onSet.testName=offset1.testName
After getting the data to be like this I want to apply one last select statement that will subtract one column from another and give me the difference. So for me it's just one step at a time.
Thanks in advance.
-KAP
I think you can use a single query with conditional aggregation:
SELECT
testName,
AVG(CASE WHEN pll = 'On' THEN elapsed ELSE 0 END) AS avgOn,
AVG(CASE WHEN pll = 'Off' THEN elapsed ELSE 0 END) AS avgOff
FROM Results
GROUP BY testName
I just saw the filemaker tag and have no idea if this work there, but on MySQL I would try something along
SELECT testName, sum(if(pll = 'On',elapsed,0)) as sumOn,
sum(if(pll = 'On',1,0)) as numOn,
sum(if(pll ='Off',elapsed,0)) as sumOff,
sum(if(pll ='Off',1,0)) as numOff,
sumOn/numOn as avgOn,
sumOff/numOff as avgOff
FROM Results
WHERE pll = 'On' or pll='Off'
GROUP BY testName ;
If it works for you then this should be rather efficient as you do not need to join. If not, thumbs pressed that this triggers another idea.
The difficulty you have with the join you envisioned is that the filtering in the WHERE clause is performed after the join was completed. So, you would still not know what records to use to compute the averages. If the above is not implementable with FileMaker then check if nested queries work. You would then
SELECT testName, on.avg as avgOn, off.avg as avgOff
FROM ( SELECT ... FROM Results ...) as on, () as off
JOIN on.testName=off.testName
If that is also not possible then I would look for temporary tables.
OK guys... thanks for the help again. Here is the final answer. The statement below is FileMaker custom function that takes 4 arguments (platform, runID, model and user count. You can see the sql statement is specified. FileMaker executeSQL() function does not support nested select statements, does not support IF statements embedded in select statements (calc functions do of course) and finally does not support the SQL keyword VALUES. FileMaker does support the SQL keyword CASE which is a little more powerful but is a bit wordy. The select statement is in a variable named sql and result is placed in a variable named result. The ExecuteSQL() function works like a printf statement for param text so you can see the swaps do occur.
Let(
[
sql =
"SELECT testName, (sum( CASE WHEN PLL='On' THEN elapsed ELSE 0 END)) as sumOn,
sum( CASE WHEN PLL='On' THEN 1 ELSE 0 END) as countOn,
sum( CASE WHEN PLL='Off' THEN elapsed ELSE 0 END) as sumOff,
sum( CASE WHEN PLL='Off' THEN 1 ELSE 0 END) as countOff
FROM Results
WHERE Platform = ?
and RunID = ?
and Model = ?
and UserCnt = ?
GROUP BY testName";
result = ExecuteSQL ( sql ; "" ; ""
; platform
; runID
; model
; userCnt )
];
getAverages ( Result ; "" ; 2 )
)
For those interested the custom function looks like this:
getAverages( result, newList, pos )
Let (
[
curValues = Substitute( GetValue( data; pos ); ","; ¶ );
sumOn = GetValue( curValues; 2 ) ;
countOn = GetValue( curValues; 3 );
sumOff = GetValue( curValues; 4 );
countOff = GetValue( curValues; 5 );
avgOn = sumOn / countOn;
avgOff = sumOff / countOff
newItem = ((avgOff - avgOn) / avgOff ) * 100
];
newList & If ( pos > ValueCount( data); newList;
getAverages( data; If ( not IsEmpty( newList); ¶ ) & newItem; pos + 1 ))
)

SQL Server 2008 view error with case statement

I cant figure out why this code executes as a query but when I execute it in a view it throws a the multi-part identifier could not be bound, error.
When I take out the case statement It works in the view, so I believe it is something that has to do with the case statement.
Any suggestions are appreciated.
WITH [cteFrostSum] AS
(
SELECT ID AS ID, theMonth as Mo,
SUM(dbo.Frost.[DRAmount]) AS [DRAmount]
FROM dbo.Frost
GROUP BY [ID], theMonth
)
SELECT DISTINCT
TOP (100) PERCENT
dbo.ternean.MemberID,
dbo.ternean.SSN,
dbo.ternean.GroupName,
dbo.ternean.CustomerID,
dbo.ternean.GroupNumber,
dbo.ternean.LastName,
dbo.Frost.DRAmount,
dbo.Frost.HittheBank,
dbo.Frost.MonthofPremium,
cte.[DRAmount] AS [SUM_Frost_Balance],
dbo.ternean.TotalCost,
cte.[DRAmount] - dbo.ternean.TotalCost AS Diff,
dbo.ternean.ACH_RoutingNo,
dbo.Frost.RTNum,
dbo.ternean.ACH_AcctNo,
dbo.Frost.AccountNumber,
CASE
WHEN dbo.Frost.RTNum <> SUBSTRING(dbo.ternean.ACH_RoutingNo, 2, 20)
THEN 'DO not match'
WHEN dbo.Frost.RTNum = SUBSTRING(dbo.ternean.ACH_RoutingNo, 2, 20)
THEN 'match'
END AS [Routing # match],
CASE
WHEN SUBSTRING(dbo.ternean.ACH_AcctNo, 2, 20) <> dbo.Frost.AccountNumber
THEN 'DO not match'
WHEN SUBSTRING(dbo.ternean.ACH_AcctNo, 2, 20) = dbo.Frost.AccountNumber
THEN 'match'
END AS [Account # match],
dbo.Frost.theMonth
FROM dbo.Frost
INNER JOIN dbo.ternean ON dbo.Frost.ID = dbo.ternean.CustomerID
AND dbo.Frost.theMonth = dbo.ternean.theMonth
INNER JOIN [cteFrostSum] cte ON dbo.Frost.ID = cte.ID
AND dbo.Frost.theMonth = cte.Mo
ORDER BY dbo.ternean.theMonth
I tried to replicate your error but couldn't.
Why are you using multi-part identifiers the field names anyway? The list of fields in the select statement can only refer to the tables in the from clause, at first reading this query seems to be referring to the tables directly in the dbo schema.
Give your tables some nice easy aliases i.e.
FROM dbo.Frost AS F
and use them like this
F.RTNum
Secondly you can simplify your case statements and only do one test i.e.:
CASE WHEN SUBSTRING(T.ACH_AcctNo, 2, 20) <> F.AccountNumber
THEN 'DO not match'
ELSE 'match'
END AS [Account # match]