Range table and IN not behaving properly - select

The program just selects everything if the carrid is ok even if it is not ok with the lt_spfli. And there aren't any entries with that carrid it gets runtime error. If I try with for all entries he just selects absolutely the entire SFLIGHT.
PARAMETERS: pa_airp TYPE S_FROMAIRP,
pa_carid TYPE S_CARR_ID.
DATA: lt_spfli TYPE RANGE OF SPFLI,
lt_sflight TYPE TABLE OF SFLIGHT.
SELECT CONNID FROM SPFLI
INTO TABLE lt_spfli
WHERE AIRPFROM = pa_airp.
SELECT * FROM SFLIGHT
INTO TABLE lt_sflight
WHERE CARRID = pa_carid AND CONNID in lt_spfli.

I just suppose, that you want every flight connection from a given airport...
Notice, that a RANGE structure has two more fields in front of the actual "compare value". So selecting directly into it will result in a very gibberish table.
Possible Solutions:
Selecting with RANGE
If you really want to use this temporary table, you can have a look at my answer here where I describe the way to fill RANGEs without any overhead. After this step, your current snippet will work the way to wanted it too. Just make sure, that it really has been filled or everything will be selected.
Selecting with FOR ALL ENTRIES
Before you use this variant you should make absolutely sure, that your specified data object is filled. Otherwise it will result in the same mess as the first solution. To do that, you could write:
* select connid
IF lt_spfli[] IS NOT INITIAL.
* select on SFLIGHT
ELSE.
* no result
ENDIF.
Selecting with JOIN
The "correct" approach in this case would be a JOIN like:
SELECT t~*
FROM spfli AS i
JOIN sflight AS t
ON t~carrid = #pa_carid
AND t~connid = i~connid
INTO TABLE #DATA(li_conns)
WHERE i~airpfrom = #pa_airp.

Use a FOR ALL ENTRIES instead of CONNID in lt_SPFLI.
As so:
SELECT *
FROM sflight
FOR ALL ENTRIES IN lt_spfli
WHERE carrid = pa_carid
AND connid = lt_spfli-connid

You are misunderstanding what a "Ranges Table" is. You fill it incorrectly.
This part of your code demonstrates the misunderstanding (with a little debug, you would see the erroneous contents immediately):
DATA: lt_spfli TYPE RANGE OF SPFLI.
SELECT CONNID FROM SPFLI INTO TABLE lt_spfli ...
A "Ranges Table" is an internal table with 4 components (SIGN, OPTION, LOW, HIGH), used in Open SQL to do complex selections on one database column (NB: it can also be used in several ABAP statements to test the value of an ABAP variable).
So, with your SQL statement, you only initialize the first component of the Ranges table, while you should transfer CONNID into the third component.
In "modern" Open SQL, you'd better do:
SELECT 'I' as SIGN, 'EQ' as OPTION, CONNID as LOW FROM SPFLI INTO TABLE #lt_spfli ...
For more information about Ranges Tables, you may refer to the answer here: What actually high and low means in a ranges table

Related

SQL query to extract default (initial) value of a value property

I am trying to create a custom template fragment that builds a table of value properties. I started by creating a SQL query fragment that pulls all properties classified by a Value Type. Now I would like to pull in the default (initial) value assigned. I figured out that it's in the Description table of t_xref, with the property guid in the client field, but I don't know how to write a query that will reliably parse the default value out since the string length may be different depending on other values set. I tried using the template content selector first but I couldn't figure out how to filter to only value properties. I'm still using the default .qeax file but will be migrating to a windows based DBMS soon. Appreciate any help!
Tried using the content selector. Successfully built a query to get value properties but got stuck trying to join and query t_xref for default value.
Edited to add current query and image
Value Properties are block properties that are typed to Value Types. I'm using SysML.
This is my current query, I am no SQL expert! I don't pull anything from t_xref yet but am pulling out only the value properties with this query:
SELECT property.ea_guid AS CLASSGUID, property.Object_Type AS CLASSTYPE, property.Name, property.Note as [Notes], classifier.Name AS TYPE
FROM t_object property
LEFT JOIN t_object classifier ON property.PDATA1 = classifier.ea_guid
LEFT JOIN t_object block on property.ParentID = block.Object_ID
WHERE block.Object_ID = #OBJECTID# AND property.Object_Type = 'Part' AND classifier.Object_Type = 'DataType'
ORDER BY property.Name
I guess that Geert will come up with a more elaborate answer, but (assuming you are after the Run State) here are some details. The value for these Run States is stored in t_object.runstate as one of the crude Sparxian formats. You find something like
#VAR;Variable=v1;Value=4711;Op==;#ENDVAR;
where v1 is the name and 4711 the default in this example. How you can marry that with your template? Not the faintest idea :-/
I can't give a full answer to the original question as I can't reproduce your data, but I can provide an answer for the generic problem of "how to extract data through SQL from the name-value pair in t_xref".
Note, this is heavily dependent on the database used. The example below extracts fully qualified stereotype names from t_xref in SQL Server for custom profiles.
select
substring(
t_xref.Description, charindex('FQName=',t_xref.Description)+7,
charindex(';ENDSTEREO',t_xref.Description,charindex('FQName=',t_xref.Description))
-charindex('FQName=',t_xref.Description)-7
),
Description from t_xref where t_xref.Description like '%FQName%'
This works using:
substring(string, start, length)
The string is the xref description column, and the start and length are set using:
charindex(substring, string, [start position])
This finds the start and end tags within the xref description field, for the data you're trying to parse.
For your data, I imagine something like the below is the equivalent (I haven't tested this). It's then a case of combining it with the query you've already got.
select
substring(
t_xref.Description, #the string to search in
charindex('#VALU=',t_xref.Description,charindex('#NAME=default',t_xref.Description)+6, #the start position, find the position of the first #VALU= tag after name=default
charindex('#ENDVALU;',t_xref.Description,charindex('#VALU=',t_xref.Description))
-charindex('#VALU=',t_xref.Description,charindex('#NAME=default',t_xref.Description))-6 #the length, find the position of the first #ENDVALU tag after the start, and subtract it from the start position
),
Description from t_xref where t_xref.Description like '%#NAME=default%' #filter anything which doesn't contain this tag to avoid "out of range" index errors

Construct SQL where clause to pull data within Power Query

Basically I want to retrieve rows of data that meet my clause conditions using Power Query.
I got 400 rows of lookup values in my spreadsheet.
Each row represent 1 lookup code for example, code AAA1, AAB2 and so on
So lets say I have a select statement and I want to construct the where clauses using the above codes so my end sql statement will look like
select * from MyTable where Conditions in ('AA1', 'AAB2')
so so far I have this
let
Source = Excel.CurrentWorkbook(){[Name="Table5"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Form ID",
Int64.Type}}),
test = Sql.Database("myserver", "myDB", [Query="SELECT * FROM myTable where" & #"Changed Type" & "])"
in
test
Obviously that didnt work but thats my pseduo scenario anyway.
Please could you advice what to do?
Thank you
Peddie
I would create a "lookup" Power Query based on the Excel table. I would set the "Load To" properties to "Only Create Connection".
Then I would start the main Query by connecting to the SQL server using the Navigator to select "MyTable". Then I would add a Merge step to the main Query, to join to the "lookup" Query, matching the "Conditions" column to the "lookup" code. I would set the Join Type to "Inner". The Merge properties window will show you visually if the 2 columns you select actually contain matching data.
This approach does not require any coding, and is easier to build, extend and maintain.
Mike Honey's join is best for your problem, but here's a more general solution if you find yourself needing other logic in your where clause.
Normally Power query only generates row filters on an equality expression, but you can put any code you want in a Table.SelectRows filter, like each List.Contains({"AA1", "AAB2"}, [Conditions])
So for your table, your query would look something like:
let
Source = Excel.CurrentWorkbook(){[Name="Table5"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Form ID", Int64.Type}}),
test = Sql.Database("myserver", "myDB"),
yourTable = test{[Name="myTable"]}[Data],
filtered = Table.SelectRows(yourTable, each List.Contains(#"Changed Type"[Form ID], [Conditions]))
in
filtered
The main downside to using the library functions is that Table.SelectRows only knows how to generate SQL where clauses for specific expression patterns, so the row filter probably runs on your machine after downloading the whole table, instead of having the Sql Server run the filter.

Converting complex query with inner join to tableau

I have a query like this, which we use to generate data for our custom dashboard (A Rails app) -
SELECT AVG(wait_time) FROM (
SELECT TIMESTAMPDIFF(MINUTE,a.finished_time,b.start_time) wait_time
FROM (
SELECT max(start_time + INTERVAL avg_time_spent SECOND) finished_time, branch
FROM mytable
WHERE name IN ('test_name')
AND status = 'SUCCESS'
GROUP by branch) a
INNER JOIN
(
SELECT MIN(start_time) start_time, branch
FROM mytable
WHERE name IN ('test_name_specific')
GROUP by branch) b
ON a.branch = b.branch
HAVING avg_time_spent between 0 and 1000)t
GROUP BY week
Now I am trying to port this to tableau, and I am not being able to find a way to represent this data in tableau. I am stuck at how to represent the inner group by in a calculated field. I can also try to just use a custom sql data source, but I am already using another data source.
columns in mytable -
start_time
avg_time_spent
name
branch
status
I think this could be achieved new Level Of Details formulas, but unfortunately I am stuck at version 8.3
Save custom SQL for rare cases. This doesn't look like a rare case. Let Tableau generate the SQL for you.
If you simply connect to your table, then you can usually write calculated fields to get the information you want. I'm not exactly sure why you have test_name in one part of your query but test_name_specific in another, so ignoring that, here is a simplified example to a similar query.
If you define a calculated field called worst_case_test_time
datediff(min(start_time), dateadd('second', max(start_time), avg_time_spent)), which seems close to what your original query says.
It would help if you explained what exactly you are trying to compute. It appears to be some sort of worst case bound for avg test time. There may be an even simpler formula, but its hard to know without a little context.
You could filter on status = "Success" and avg_time_spent < 1000, and place branch and WEEK(start_time) on say the row and column shelves.
P.S. Your query seems a little off. Don't you need an aggregation function like MAX or AVG after the HAVING keyword?

Filling additional columns of an internal table with additional data by SELECT statement? Can this be done?

SELECT matnr ersda ernam laeda
FROM mara
INTO CORRESPONDING FIELDS OF TABLE gt_mara
UP TO 100 ROWS.
At this point I have 100 entries in the itab gt_mara.
SELECT aenam vpsta pstat lvorm mtart
FROM mara
INTO CORRESPONDING FIELDS OF TABLE gt_mara
FOR ALL ENTRIES IN gt_mara
WHERE matnr = gt_mara-matnr AND
ersda = gt_mara-ersda AND
ernam = gt_mara-ernam AND
laeda = gt_mara-laeda.
At this point I have 59 entries. Which makes sense. This code is buggy, because it might be modifying the selection criteria at run time.
Anyway what i intended was this: select the first 4 fields of the table at one point, and then select the other 5 at some other.
Of course, this is just an example. Perhaps the second select would be done on a different table with the same key or with a different number of fields.
So can this even be done?
Are there more efficient methods to achieve this than what comes to my mind by default (redoing the complete select) ?
Ok I think the essence of your question is more about whether you can update certain unfilled fields in an internal table directly through a second select statement.
The answer is no. Your second select statement would replace the contents in table gt_mara, so you would be left with an internal table where the first 4 fields are blank, and the last 5 are filled.
The best you could do is something like this:
SELECT matnr ersda ernam laeda
FROM mara
INTO CORRESPONDING FIELDS OF TABLE gt_mara
UP TO 100 ROWS.
SELECT matnr aenam vpsta pstat lvorm mtart
FROM mara
INTO CORRESPONDING FIELDS OF TABLE gt_mara2
FOR ALL ENTRIES IN gt_mara
WHERE matnr = gt_mara-matnr AND
ersda = gt_mara-ersda AND
ernam = gt_mara-ernam AND
laeda = gt_mara-laeda.
loop at gt_mara2 into ls_mara.
modify gt_mara from ls_mara transporting aenam vpsta pstat lvorm mtart
where matnr = ls_mara-matnr.
endloop.
This is obviously quite inefficient, which is why you would always try to make the database do as much of the work for you before you bring the data back to the application server. Obviously if the data is coming from the same table selecting it all in one go is going to be your best option. In most cases even if the data is in different tables you would be better off creating a view or using a join.
In rare cases it is necessary to loop at your internal table to fill in some fields that were not available to you when you did the original select.
Either SELECT everything you need right away (which is the preferred solution if the data comes from the same table) or SELECT the additional stuff later (which is a good idea if the stuff comes from a different table that is not used for the first selection). For assembling the result set, the database usually needs to access the entire dataset anyway, so it doesn't really hurt to select some additional fields - in contrast to hitting the database again with a massive SELECT statement (if the FOR ALL ENTRIES table gets large). Also bear in mind that - depending on the kind of processing you're doing - the contents of the table might have changed in the meantime. If the database transaction (LUW) ends (which is always the case between dialog steps), you loose the database-level transaction isolation.

Link tables when one column is padded with 0's in Crystal Reports

I have a database that has two tables that need to be linked, but in one table the data is padded with zeros. For example, the tables may look like this:
CUSTOMER.CUSTNUM = 00000000123456
CUSTOMERPHONE.CUSTNUM = 123456
I can't figure out how to get these tables to properly join.
What I'm trying to do now is trick Crystal Reports into specifying the Join clause by adding the following to the selection expert:
Right ({CUSTOMER.CUSTNUM}) = {CUSTOMERPHONE.CUSTNUM}
That's not working though, and I get no records at all in my report.
Any ideas?
Crystal doesn't like heterogeneous joins.
Options:
use a command object, which will give you more control over the linkage
create a SQL Expression that performs the desired concatination; link fields in the record-selection formula
use a subreport for the linked table
alter the table to make the data types compatible
create a SQL view that performs the joins
First thing, why does CUSTOMER.CUSTNUM have leading zeros in the first place? It seems to me that it should be a NUMERIC data type instead of a VARCHAR. CUSTNUM should be consistent in all of the tables. Just a thought.
Anyway, to answer your question, you could try creating a SQL Command in Crystal to join the two tables. In the join, just use your database's function for converting from a varchar to a number. For example, in Access you could do:
SELECT *
FROM `Customer`
LEFT OUTER JOIN `Orders` ON `Orders`.`Numeric Customer ID` = CLng(`Customer`.`Varchar Customer ID`)
If fastest performance isn't an issue, you can accomplish this using Select Expert. I think the problem is your formula.
Try changing your formula from this:
{CUSTOMERPHONE.CUSTNUM} = Right({CUSTOMER.CUSTNUM})
to this:
{CUSTOMERPHONE.CUSTNUM} = Right({CUSTOMER.CUSTNUM}, Length({CUSTOMERPHONE.CUSTNUM}))