How to Left Inner Join two queries in Sybase? - sybase

I have two queries that should be joined together. Here is my query 1:
SELECT
t1.rec_id,
t1.category,
t1.name,
t1.code,
CASE
WHEN t1.name= 'A' THEN SUM(t1.amount)
WHEN t1.name = 'D' THEN SUM(t1.amount)
WHEN t1.name = 'H' THEN SUM(t1.amount)
WHEN t1.name = 'J' THEN SUM(t1.amount)
END AS Amount
FROM Table1 t1
GROUP BY t1.name, t1.rec_id, t1.category, t1.code
Query 1 produce this set of results:
Rec ID Category Name Code Amount
1 1 A MIX 70927.00
1 3 D MIX 19922.00
1 2 H MIX 55104.00
1 4 J MIX 76938.00
Then I have query 2:
SELECT
CASE
WHEN t2.category_id = 1 THEN SUM(t2.sum)
WHEN t2.category_id = 2 THEN SUM(t2.sum)
WHEN t2.category_id = 3 THEN SUM(t2.sum)
WHEN t2.category_id = 4 THEN SUM(t2.sum)
END AS TotalSum
FROM Table2 t2
INNER JOIN Table1 t1
ON t1.amnt_id = t2.amnt_id
AND t2.unique_id = #unique_id
GROUP BY t2.category_id
The result set of query 2 is this:
TotalSum
186013.00
47875.00
12136.00
974602.00
All I need is this result set that combines query 1 and query 2:
Rec ID Category Name Code Amount TotalSum
1 1 A MIX 70927.00 186013.00
1 3 D MIX 19922.00 47875.00
1 2 H MIX 55104.00 12136.00
1 4 J MIX 76938.00 974602.00
As you can see there is connection between table 1 and table 2. That connection is amnt_id. However, I tried doing LEFT INNER JOIN on query 1 and then simply using same logic with case statement to get the total sum for table 2. Unfortunately Sybase version that I use does not support Left Inner Join. I'm wondering if there is other way to join these two queries? Thank you

I wondered if the CASE statement makes sense in the first query because it sums in every row. Are there other values for the name column except A, D, H, J? If not you can change the CASE statement to SUM(t1.amount) AS Amount. Also the GROUP BY in the first query seems dubious to me: you are grouping by the record id column - that means you are not grouping at all but instead return every row. If that is what you really want you can omit the SUM at all and just return the pure amount column.
As far as I understood your problem and your data structure: the values in Table2 are kind of category sums and the values in Table1 are subsets. You would like to see the category sum for every category in Table1 next to the single amounts?
You would typically use a CTE (common table expression, "WITH clause") but ASE doesn't support CTEs, so we have to work with joins. I recreated your tables in my SQL Anywhere database and put together this example. In a nutshell: both queries are subqueries in an outer query and are left joined on the category id:
SELECT *
FROM
(
SELECT
t1.rec_id,
t1.category,
t1.name,
t1.code,
CASE
WHEN t1.name= 'A' THEN SUM(t1.amount)
WHEN t1.name = 'D' THEN SUM(t1.amount)
WHEN t1.name = 'H' THEN SUM(t1.amount)
WHEN t1.name = 'J' THEN SUM(t1.amount)
END AS Amount
FROM Table1 t1
GROUP BY t1.rec_id, t1.name, t1.category, t1.code
) AS t1
LEFT JOIN
(
SELECT category_id, SUM(sum) FROM
table2
GROUP BY category_id
) AS totals(category_id, total_sum)
ON totals.category_id = t1.category;
This query gives me:
Rec ID Category Name Code Amount Category_id total_sum
2 3 D MIX 19922.00 3 47875.00
3 2 H MIX 55104.00 2 12136.00
1 1 A MIX 70927.00 1 186013.00
4 4 J MIX 76938.00 4 974602.00
You surely have to tweak it a bit including your t2.unique_id column (that I don't understand from your queries) but this is a practical way to work around ASE's missing CTE feature.
BTW: it's either an INNER JOIN (only the corresponding records from both tables) or a LEFT (OUTER) JOIN (all from the left, only the corresponding records from the right table) but a LEFT INNER JOIN makes no sense.

Related

Selecting TOP #Number From "Group"

I think my attempt of wording this is going to be more difficult than sharing the code; However...
Firstly, I'm using SQL Server and extracting information from SAP B1.
I'm trying to generate a CSV file from my SQL Dataset to import into a logistics solution.
OrderNo
NumberOfItems
ItemNumber
12345
2
1
12345
2
2
45678
3
1
45678
3
2
45678
3
3
Using the above table, Order number 12345 would be a TOP 2 function, Order number 45678 would be a TOP 3 function.
The #TOPNUMBER will be pulled from an invoice within SAP so it will be dynamic for each Order number.
When I've writte my query I have performed an inner join using <> to bring through thousands of individual results, then filtering down using the TOP function. (which may have been my first mistake)
DECLARE #NUM AS INT = 3
SELECT TOP (#NUM) --Top Function to be replaced with "Number Of ITems"
T0.[DocNum] AS 'Order_No',
ISNULL(T0.NumAtCard,'') AS 'Cust_Ord_No',
Convert(varchar,T0.DocDate,103) AS 'Order_Date',
'Delivery' AS 'Order_Type',
T0.CardName as 'Del_Name',
ISNULL(T12.buIldingS,'') AS 'Del_Addr_1',
ISNULL(T12.BlockS,'') AS 'Del_Addr_2',
ISNULL(T12.StreetS,'') AS 'Del_Addr_3',
ISNULL(T12.CityS,'') AS 'Del_Town',
ISNULL(T12.CountyS,'') AS 'Del_County',
ISNULL(T12.ZipCodeS,'') AS 'Postal_code',
ISNULL(T12.CountryS,'') AS 'Ctry_Code',
ISNULL(t5.Tel1,'') AS 'Contact_Tel_1',
ISNULL(T5.Tel2,'') AS 'Contact_Tel_2',
ISNULL(T5.Cellolar,'') AS 'Contat_Tel_Mob',
ISNULL(T5.E_mailL,'') AS 'Contact_Email',
ISNULL(T0.Comments,'') AS 'Order_Notes',
'00:05' AS 'Unload_Time',
'' AS 'Delivery_Method',
convert(varchar,DateAdd(Day,-7,GETDATE()),103) AS 'Required_Date',
ISNULL('10:00','') AS 'Required_Time_F', ---- Field to be created in sap
ISNULL('16:00','') AS 'Required_Time_T', ---- Field to be created in sap
Concat('Item ',Format(T1.DocEntry,'000')) as 'Item_Code',
Concat('Item ',Format(T1.DocEntry,'000'),' Of ',FORMAT(#NUM,'000')) as 'Item_Desc',
Concat(T0.DocNum,Format(T1.DocEntry,'000'),FORMAT(#NUM,'000')) AS 'package_id',
'1' AS Item_Qty,
T0.Weight/#NUM AS 'Item_Wgt',
'' AS 'Item_Cube',
convert(varchar,DateAdd(Day,-7,GETDATE()),103) AS 'Item_OnHand_Date'
FROM OINV T0 INNER JOIN INV12 T12 ON T0.DocEntry = T12.DocEntry INNER JOIN OCPR T5 ON T5.[CntctCode] = T0.[CntctCode] INNER JOIN ORDR T1 ON T1.DocNum <> T0.DocEntry
WHERE T0.DocNum > '1647674' AND T0.TrnspCode = 1
ORDER BY T1.DocEntry
This code worked perfectly when I was using WHERE T0.DocNum = '1647674' AND T0.TrnspCode = 1. As soon as I've changed it to a where clause with multiple DocNums it's falling flat on its face.
I'm thinking the easier option may be around me removing the Select Top and the Inner join on ORDR and OINV, and using my #NUM variable (from each document) to only actually give me the required number of lines instead of creating lots and filtering it down?
Any help will be appreciated as it took me a good couple of hours to get this to export in a format in which the CSV file takes with no issues!
---- EDIT ---- Update on the code, i think i've got somewhere close by tweaking a few things, i've actually changed the <> join on the ordr table, to a <= join relating to the variable i'm declaring..
I've ditched the variable number, and replaced it with a dynamic field within SAP.
SELECT
T0.[DocNum] AS 'Order_No',
ISNULL(T0.NumAtCard,'') AS 'Cust_Ord_No',
Convert(varchar,T0.DocDate,103) AS 'Order_Date',
'Delivery' AS 'Order_Type',
T0.CardName as 'Del_Name',
ISNULL(T12.buIldingS,'') AS 'Del_Addr_1',
ISNULL(T12.BlockS,'') AS 'Del_Addr_2',
ISNULL(T12.StreetS,'') AS 'Del_Addr_3',
ISNULL(T12.CityS,'') AS 'Del_Town',
ISNULL(T12.CountyS,'') AS 'Del_County',
ISNULL(T12.ZipCodeS,'') AS 'Postal_code',
ISNULL(T12.CountryS,'') AS 'Ctry_Code',
ISNULL(t5.Tel1,'') AS 'Contact_Tel_1',
ISNULL(T5.Tel2,'') AS 'Contact_Tel_2',
ISNULL(T5.Cellolar,'') AS 'Contat_Tel_Mob',
ISNULL(T5.E_mailL,'') AS 'Contact_Email',
ISNULL(T0.Comments,'') AS 'Order_Notes',
'00:05' AS 'Unload_Time',
'' AS 'Delivery_Method',
convert(varchar,DateAdd(Day,-7,GETDATE()),103) AS 'Required_Date',
ISNULL('10:00','') AS 'Required_Time_F', ---- Field to be created in sap
ISNULL('16:00','') AS 'Required_Time_T', ---- Field to be created in sap
Concat('Item ',Format(T1.DocEntry,'000')) as 'Item_Code',
Concat('Item ',Format(T1.DocEntry,'000'),' Of ',FORMAT(T0.U_PACKQTY,'000')) as 'Item_Desc',
Concat(T0.DocNum,Format(T1.DocEntry,'000'),FORMAT(T0.U_PACKQTY,'000')) AS 'package_id',
'1' AS Item_Qty,
T0.Weight/T0.U_PACKQTY AS 'Item_Wgt',
'' AS 'Item_Cube',
convert(varchar,DateAdd(Day,-7,GETDATE()),103) AS 'Item_OnHand_Date'--,
FROM OINV T0 INNER JOIN INV12 T12 ON T0.DocEntry = T12.DocEntry INNER JOIN OCPR T5 ON T5.[CntctCode] = T0.[CntctCode] INNER JOIN ORDR T1 ON T1.DocEntry <= T0.U_PACKQTY
WHERE T0.DocNum IN ('1647775','1647777','1647778')
The WHERE T0.DocNum IN will be replaced with another where clause to look for "Van Delivery" transport types, and DocNum which will be greater than previously exported DocNum, this will probably be by inserting the "MAX" DocNum into a table at the time i export it, and then using this "MAX" Docnum as a "Min" value in my where clause, so this should always look for new orders only. (I think!)
I think DocNum is of type varchar

Understand below SQL Query

I need your help to understand below query can anyone help me to describe it, i wanted to know the role of b.id is null and r.id is null in below query if anyone can explain whole code then it would be great?
select l.id as start,
(
select min(a.id) as id
from sequence as a
left outer join sequence as b on a.id = b.id - 1
where b.id is null
and a.id >= l.id
) as end
from sequence as l
left outer join sequence as r on r.id = l.id - 1
where r.id is null;
This query returns your "islands"of your sequence, i.e. start and end of continuos id intervals.
You can read more on gaps and islands here: Special Islands and here The SQL of Gaps and Islands in Sequences
The query is finding islands of consecutive numbers, outputting the start and end of ranges where all consecutive numbers are there, so for the set if numbers
{1,3,4,5,6,9,10}
I would expect
1,1
2,6
9,10
to be selected
The outer query starts by finding number (N) that cannot join to a record holding N-1, detected by r.id is null
The sub query then finds the next highest number (M) that does not join to a record holding M+1 (detected using b.id is null)
so in my example 3 does not have a '2' to join to, meaning 3 begins a range. The first number >= to that with no subsequent record is 6, which has no '7' to make a join to
The query is joining two tables (in this case the same table but it does not matter) using an outer join.
if we have
SELECT t1.a, t1.b, t2.d
FROM table1 t1
LEFT OUTER JOIN table2 t2 ON t1.a = t2.a
WHERE t2.a is null
This means it returns a set of records with all those from table1, however its possible there will not be a joined record from table2 for every record in table1. If there isn't the table2 fields are returned as null so the the WHERE clause is effectively saying return me all records from table1 where we do not have a join record in table2.
In your example where it joins onto itself you are looking where there is not a next/previous record (depending upon which way you are looking at it) based upon the id e.g. if id = 5, then there is not a record with id = 4
Overall the sql as a whole looks like its returning the consecutive id ranges in the sequence table.

SQL Join Including NULLs

Using SQL Server Management Studio 2016 (v 13.0).
I have two tables with two distinct keys I can use to join, with the catch being that there are mixed NULLs in both PK columns:
Table 1 Table 2
Acct1 App1 Acct2 App2 Product
----------- ----------------------
1 A NULL A Bed
2 B 2 B Sofa
3 C 3 NULL Bed
4 D 4 D Bed
Desired result in the joined table, only including those where Product = Bed:
Acct App Product
1 A Bed
3 C Bed
4 D Bed
Thank you!
While I agree #d219's answer should be the correct solution, a different approach could use an or in the join such like:
select Acct1,App1,Product
from table1 inner join table2
on App1=App2 or Acct1=Acct2
where Product='Bed'
See this post for discussion on using the or join.
One way would be to do two separate SELECT statements using each key and then UNION them, something like this:
SELECT t1.Acct1, t1.App1, t2.Product
FROM Table1 t1
INNER JOIN Table2 t2
ON t1.Acct1 = t2.Acct2
WHERE t2.Product = 'Bed'
UNION
SELECT t1.Acct1, t1.App1, t2.Product
FROM Table1 t1
INNER JOIN Table2 t2
ON t1.App1 = t2.App2
WHERE t2.Product = 'Bed'

Select Count Top Inner Join and Where Clause in SQL

This is my Query:
SELECT TOP 3 tablestudentanswer.examid,
tablestudentanswer.studentid,
tablestudentanswer.itemno,
tablestudentanswer.studentanswer,
tablescore.score
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
WHERE tablestudentanswer.examid = 1
AND tablestudentanswer.itemno = 1
ORDER BY tablescore.score ASC
It returns this table:
ExamID StudentID ItemNo StudentAnswer Score
1006 1 1 A 25
1005 1 2 B 30
1004 1 3 A 35
What i want to do is it will return 2 if StudentAnswer='A' and 1 if StudentAnswer='B'
Guys there is nothing wrong with my query on top. What i am asking is what should I add in that query.
I have this which in my mind should return 2 but its an error.
Select COUNT(*) From (
Select Top 3 TableStudentAnswer.ExamID, TableStudentAnswer.StudentID, TableStudentAnswer.ItemNo, TableStudentAnswer.StudentAnswer, TableScore.Score
from TableStudentAnswer
Inner join TableScore on TableStudentAnswer.StudentID=TableScore.StudentID and TableStudentAnswer.ExamID=TableScore.ExamID
where TableStudentAnswer.ExamID=1 and TableStudentAnswer.ItemNo=1
Order By TableScore.Score Asc) where TableStudentAnswer.StudentAnswer = 'A'
It should return:
2
Please help me!
Will this do?
SELECT TOP 3 tablestudentanswer.examid,
tablestudentanswer.studentid,
tablestudentanswer.itemno,
tablestudentanswer.studentanswer,
tablescore.score,
case
when tablestudentanswer.studentanswer = 'A' then 2
when tablestudentanswer.studentanswer = 'B' then 1
else NULL
end as [MyColumn]
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
WHERE tablestudentanswer.examid = 1
AND tablestudentanswer.itemno = 1
ORDER BY tablescore.score ASC
Your question is a bit unclear. Perhaps you want the amount of answers for each?
count(1) over (partition by tablestudentanswer.studentanswer)
This will give you a column with the amount of all the answers with the given studentanswer to each of the rows in the result set. However, note that this could be quite slow. If you can, you're better off using a normal group by.
Do you mean you would like the query to return the number of answers? If so, using COUNT may help.
SELECT tablestudentanswer.studentid,
tablestudentanswer.studentanswer
COUNT(1) AS NumberOfAnswers
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
GROUP BY tablestudentanswer.studentid, tablestudentanswer.studentanswer
Please correct me if I am wrong.
By the way, why does your result table doesn't consist of itemno even though you have it in your SELECT statement?

Joining two queries horizontally

I wrote two queries below that produce one row of data each.
What is the best way to combine them such that I am LEFT with only a single row of data?
These are coming FROM two DISTINCT databases named : [ASN01] and [dsi_ASN_dsicx]
I have 70 pairs of databases like this but am showing only one for simplicity.
The fact that the three letter acronym ASN is common to both database names is no mistake and if needed can be a part of the solution.
Current Results:
Site, Elligence (header)
ASN, 100.00
Site, GP_Total (header)
ASN, 120.00
Desired results:
Site, GP_Total, Elligence (header)
ASN, 120.00, 100.00
SELECT 'ASN' AS Site ,
CASE SUM(perdblnc)
WHEN NULL THEN 0
ELSE -1 * SUM(PERDBLNC)
END AS GP_Total
FROM [ASN01].[dbo].[GL10110] T1
LEFT OUTER JOIN [ASN01].[dbo].[GL00105] T2 ON [T1].[ACTINDX] = [T2].[ACTINDX]
WHERE YEAR1 = 2012
AND PERIODID IN ( '2' )
AND ACTNUMST IN ( '4200-0000-C', '6940-0000-C', '6945-0000-C',
'6950-0000-C' )
SELECT 'ASN' AS [Site] ,
SUM(pi.amount) AS [Elligence]
FROM [dsi_ASN_dsicx].dbo.charge c
LEFT JOIN [dsi_ASN_dsicx].dbo.paymentitem pi ON c.idcharge = pi.chargeid
LEFT JOIN [dsi_ASN_dsicx].dbo.payment p ON pi.paymentid = p.idpayment
LEFT JOIN [dsi_ASN_dsicx].dbo.paymenttype pt ON p.paymenttypeid = pt.idpaymenttype
WHERE pi.amount != 0
AND pt.paymentmethod NOT IN ( '5', '7' )
AND pt.paymentmethod IS NOT NULL
AND p.sdate >= '20120201'
AND p.sdate <= '20120229'
WIthout going through and changing any of your queries, the easiest way would be to use temp tables using the "WITH" common_table_expression. Table1 and Table2 are temp tables created from your select statements. Therefore, we select table1 and join table2.
Let me know if there are any syntax problems, I don't have anything to test this on presently.
;With Table1 as (SELECT 'ASN' as Site, Case sum(perdblnc)
WHEN NULL THEN 0
ELSE -1*sum(PERDBLNC) END as GP_Total
FROM [ASN01].[dbo].[GL10110] T1
Left Outer Join [ASN01].[dbo].[GL00105] T2
ON [T1]. [ACTINDX]= [T2]. [ACTINDX]
WHERE YEAR1 = 2012
AND PERIODID in ('2')
AND ACTNUMST in ('4200-0000-C', '6940-0000-C', '6945-0000-C', '6950-0000-C'))
, Table2 as (SELECT
'ASN' as [Site],
SUM(pi.amount) as [Elligence]
FROM [dsi_ASN_dsicx].dbo.charge c
LEFT JOIN [dsi_ASN_dsicx].dbo.paymentitem pi on c.idcharge = pi.chargeid
LEFT JOIN [dsi_ASN_dsicx].dbo.payment p on pi.paymentid = p.idpayment
LEFT JOIN [dsi_ASN_dsicx].dbo.paymenttype pt on p.paymenttypeid = pt.idpaymenttype
WHERE pi.amount != 0
AND pt.paymentmethod not in ('5','7')
AND pt.paymentmethod is not null
AND p.sdate >='20120201' and p.sdate <= '20120229')
SELECT * FROM Table1
LEFT JOIN Table2 ON Table1.site = Table2.site
Hope this helps! Marks as answer if it is =)

Resources