How to get row count of database table - abap

I am just new in abap language and I am trying to practice an inner join statement but I don't know how whether I will be able to get the number of rows of my select statement before output.
Here's what I want to achieved.
<--------------------------------------- >
< total number of rows > Record(s) found |
Column Header 1|Column Header 2 ..
< data
....
retrieved >
<--------------------------------------- >
Below is my select statement :
SELECT spfli~carrid scarr~carrname sflight~planetype sflight~fldate sflight~price spfli~cityfrom spfli~cityto
INTO (g_carrid ,g_carrname ,g_planetype,g_fldate ,g_price ,g_cityfrom ,g_cityto) FROM spfli
INNER JOIN sflight
ON spfli~carrid = sflight~carrid AND spfli~connid = sflight~connid
INNER JOIN scarr
ON scarr~carrid = spfli~carrid
WHERE spfli~carrid = s_carrid-low.
WRITE: / g_carrname ,g_planetype,g_fldate ,g_price ,g_cityfrom ,g_cityto.
ENDSELECT.
And if you have any advice and idea on how to do this using internal table, please, show me a sample. I just really want to learn. Thank you and God Bless.

The system variable SY-DBCNT should give you the number of rows selected, but only after the select ends.
The alternative to SELECT-ENDSELECT is to select all the rows at once with SELECT INTO TABLE into an internal table (provided you are not selecting too much at once!).
For example:
data: lt_t000 type table of t000.
select * from t000 into table lt_t000.
This will select everything from that table in one go into the internal table. So what you could do is to declare an internal table with all the fields currently in your INTO clause and then specify INTO TABLE for your internal table.
After the SELECT executes, SY-DBCNT will contain the number of selected rows.
Here is a complete example, built around the SELECT statement in your question, which I have not checked for sanity, so I hope it works!
tables: spfli.
select-options: s_carrid for spfli-carrid.
* Definition of the line/structure
data: begin of ls_dat,
carrid type s_carr_id,
carrname type s_carrname,
planetype type s_planetye,
fldate type s_date,
price type s_price,
cityfrom type s_from_cit,
cityto type s_to_city,
end of ls_dat.
* Definition of the table:
data: lt_dat like table of ls_dat.
* Select data
select spfli~carrid scarr~carrname sflight~planetype sflight~fldate sflight~price spfli~cityfrom spfli~cityto
into table lt_dat
from spfli
inner join sflight
on spfli~carrid = sflight~carrid and spfli~connid = sflight~connid
inner join scarr
on scarr~carrid = spfli~carrid
where spfli~carrid = s_carrid-low.
* Output data
write: 'Total records selected', sy-dbcnt.
loop at lt_dat into ls_dat.
write: / ls_dat-carrid, ls_dat-carrname, ls_dat-planetype, ls_dat-fldate, ls_dat-price, ls_dat-cityfrom, ls_dat-cityto.
endloop.
Note: Report (type 1) programs still support the notion of declaring internal tables with header lines for backward compatibility, but this is not encouraged! Hope it works!

If you only need row count without retrieving data itself the following syntax works as well
SELECT COUNT(*)
FROM spfli
INNER JOIN sflight
...
After execution of this query you will be able to get row count value from SY-DBCNT and DB load will be much less than during usual SELECT ... INTO itab.
This is, however, true only if you don't need actual data. If you need both row count and data itself it is not sensible to split this into separate select statement.

Related

Using Select * in a SQL JOIN returns the wrong id value for the wrong table

I have two tables (PlayerDTO and ClubDTO) and am using a JOIN to fetch data as follows:
SELECT * FROM PlayerDTO AS pl
INNER JOIN ClubDTO AS cl
ON pl.currentClub = cl.id
WHERE cl.nation = 7
This returns the correct rows from PlayerDTO, but in every row the id column has been changed to the value of the currentClub column (eg instead of pl.id 3,456 | pl.currentClub 97, it has become pl.id 97 | pl.currentClub 97).
So I tried the query listing all the columns by name instead of Select *:
SELECT pl.id, pl.nationality, pl.currentClub, pl.status, pl.lastName FROM PlayerDTO AS pl
INNER JOIN ClubDTO AS cl
ON pl.currentClub = cl.id
WHERE cl.nation = 7
This works correctly and doesn’t change any values.
PlayerDTO has over 100 columns (I didn’t list them all above for brevity, but I included them all in the query) but obviously I don’t want to write every column name in every query.
So could somebody please explain why Select * changes the id value and what I need to do to make it work correctly? All my tables have a column called id, is that something to do with it?
SELECT *... is, according to the docs...
shorthand for “select all columns.” (Source: Dev.MySQL.com
Both your tables have id columns, so which should be returned? It's not indicated, so MySQL makes a guess. So select what you want to select...
SELECT pl.id, *otherfieldsyouwant* FROM PlayerDTO AS pl...
Or...
SELECT pl.* FROM PlayerDTO AS pl...
Typically, SELECT * is bad form. The odds you are using every field is astronomically low. And the more data you pull, the slower it is.

Wildcards in GROUP BY clause in OpenSQL?

I have a select similar to the one below:
SELECT DISTINCT
SCARR~CARRID,
SCARR~CARRNAME,
MIN( SPFLI~DISTANCE ) AS MIN_DISTANCE
FROM SCARR JOIN SPFLI ON SPFLI~CARRid = SCARR~CARRid
GROUP BY
SCARR~CARRID,
SCARR~CARRNAME
INTO TABLE #DATA(result).
In the real case, these are other tables and I have many more fields in both SELECT and GROUP BY.
Can I simplify the GROUP BY to not have to write again all the fields that are in the SELECT clause from the table SCARR?
I know other options are to use MIN for all the fields of table SCARR except its ID, or not GROUP BY and instead remove duplicates after the select, but I was trying to do something like GROUP BY scarr~*
No, the OpenSQL syntax doesn't support this.
Here is one possible workaround using dynamic approach:
DATA: lcl_struc TYPE REF TO cl_abap_structdescr,
lt_grouped TYPE TABLE OF sflight.
lcl_struc ?= cl_abap_typedescr=>describe_by_name( 'SFLIGHT' ).
DATA(group_by) = REDUCE string( INIT line TYPE char1024 FOR <field> IN lcl_struc->get_components( ) NEXT line = COND #( WHEN line <> space THEN line && `, ` && `SFLIGHT~` && <field>-name ELSE line && `SFLIGHT~` && <field>-name ) ).
SELECT (group_by)
FROM scarr LEFT OUTER JOIN spfli
ON spfli~carrid = scarr~carrid
LEFT OUTER JOIN sflight
ON sflight~carrid = spfli~carrid
AND sflight~connid = spfli~connid
LEFT OUTER JOIN sairport
ON sairport~id = spfli~airpfrom
WHERE scarr~carrid = 'AA'
GROUP BY (group_by)
INTO CORRESPONDING FIELDS OF TABLE #lt_grouped.
Pay attention to several limitations here:
In the above sample I group only by SFLIGHT fields, because when specifying wildcarded fields together with single (SELECT spfli~*, sflight~carrid) the inline result table will have not single fields but sub-structures, so it's more sophisticated for processing or requires explicit declaration. Keep this in mind if you need group by joined tables.
As it stated in help chapter provided by Florian, dynamic group clause requires all-or-nothing:
The columns after SELECT must then be specified either solely as arguments of aggregate functions or only directly. If not, this would raise a handleable exception CX_SY_OPEN_SQL_DB. Invalid syntax raises a handleable exception from the class CX_SY_DYNAMIC_OSQL_ERROR.
So it may be useless in your particular case if you need one-field aggregation, but just in case I put it here.
No is not possible in plain OpenSQL.
You can create a new CDS view for aggregate SPFLI, the view return for each CARRID the minimum distance:
define view Z_CDS_SPFLI_MIN as select from SPFLI
{
carrid,
min( distance ) as min_distance
}
group by carrid
And then you can modify your query like this, without use group-by:
select SCARR~*, Z_CDS_SPFLI_MIN~min_distance
from SCARR
inner join Z_CDS_SPFLI_MIN
on SCARR~carrid = Z_CDS_SPFLI_MIN~carrid.

Inner join only with TABLES declaration

I need to write a ABAP program using an inner join on the tables mara and makt. I understand the idea of using data declarations like this:
data: imatnr type mara-matnr,
ematnr type makt-matnr.
select mara~matnr makt~matnr into (imatnr, ematnr) from mara left join makt on mara~matnr = makt~matnr.
write: / imatnr, ematnr.
endselect.
In my exams I have to write a ABAP program without internal tables, field symbols JUST TABLES DEKLARATIONS.
I made several attempts to do this but I found no answer.
I tried something like this:
tables: mara, makt.
select * from mara inner join makt on mara~matnr = makt~matnr.
write: / mara-matnr, makt-matnr.
endselect.
The console says that I have to use an INTO if I want to use an JOIN within a select * from statement.
So my question is: Is it possible to build an JOIN only with TABLES or do I need DATA: anyways?
So my question is:
Is it possible to build an JOIN only with tables: mara, makt.
No.
Table is a work area aka flat structure, keyword is flat. You cannot select multiple rows only with TABLES and cannot join TABLES workareas.
BTW, TABLES help guidelines clearly says
No table work areas except for classic dynpros
so forget this obsolete stuff.
If your exams and your learning courses require you to use only TABLES then run away from those course and those school. Please, run away.
P.S. The only way you can fulfill your nasty requirement is nested SELECTs:
TABLES: mara, makt.
SELECT * FROM mara
INTO mara.
WRITE: / `MARA selected: `, mara-matnr.
SELECT *
INTO makt
FROM makt WHERE matnr = mara-matnr.
WRITE: / `MAKT selected: `, makt-matnr.
ENDSELECT.
ENDSELECT.
But that feels disgusting.
Few distinctions between SQL selection queries that might help you.
A generic select statement (without any particular type) looks like this.
Select * from [table] where [field] = [value]
Depending on whether or not and how you specify into clause a certain type of select is used.
into table
into table selects one or multiple records into internal table. can only be used when your select clause (field list) is identical to structure of your internal table.
data: lt_ekko type table of ekko,
ls_ekko type table of ekko,
select * from ekko into table #lt_ekko.
loop at lt_ekko into ls_ekko.
write: / ls_ekko-ebeln.
endloop.
into
into means you are selecting into a variable (if you are selecting 1 column) or a structure (if you are selecting more than 1). This is important, because structures can only and variables only store 1 value or row, which means you must specify that you are selecting a single row or use select/endselect statements to perform looping select.
data: ls_ekko type ekko.
select single * from ekko into #ls_ekko where ebeln = [some number]
write: / ls_ekko-ebeln.
OR
select * from ekko into #ls_ekko where ebeln = [some number].
write: / ls_ekko-ebeln. "will print one for each row
endselect.
into corresponding fields of (table)
into corresponding fields of (and into corresponding fields of table) selects records the same way into and into internal table. The difference is that your structure or internal table does not have to be identical to your selected field list. Selected fields will be stored into your table/structure fields with same names.
data: ls_ekko type ekko.
select single ebeln, bukrs from ekko into #ls_ekko where ebeln = [some number].
write: / ls_ekko-ebeln.
no into clause
no into clause is similar to into [structure], because it can only select 1 record. That means you have to specify that you select a single record.
Note: Tables you are selecting from must be declared in the program to use this type of select.
select single * from ekko where ebeln = [some number].
write: / ekko-ebeln.
Since you are not using into clause, you are using the last type, and that means you must use select single

SQL SERVER- Lookup a derived column in another table and return a value

I have a query which has a column (PROCESS) which was obtained using a concat function. Now i need to lookup this column in a column which is on another table (Table2) and then return a value from the same table (table 2).
Query:
My Current output will look like this.
I have a reference Table like this.
I need to lookup "Process" (Query Result) in "Type" (the reference Table) and return "Description" (Ref table) in "Process Column".
Final Output should look like this
I'm unable to figure out how to modify my query to do this. pls help.
You need a join. Join your reference table to your other query based on the concatenated value and you can display the description you are looking for.
FROM #Source As s) D INNER JOIN [Reference Table] AS rt ON d.Process = rt.type
You need an INNER JOIN between Source and Reference tables for your nested query with the JOIN condition as in the following :
SELECT ... -- all columns of your queries outer part
(
SELECT s.Date, s.Station, s.worktype, s.tasktype, description as process, ....
FROM source s
INNER JOIN reference r on ( concat(s.worktype,s.tasktype) = r.type )
) D
GROUP BY D.date, D.station, D.worktype, D.accountno;
SQL Fiddle Demo

Setting up database structure

I am trying to figure out the best way to restructure my database as I didn't plan ahead and now I am a little stuck on this part :)
I have a Table called Campaigns and a Table called Data Types.
Each campaign is a unique record that holds about 10 fields of data.
The data types contains 3 fields - ID, Type, Description
When You create a campaign, you can select as many data types as you would like.
1, 2 or all 3 of them.
My concern / question is - How can I store what the user selected with the campaign record?
I need to be able to pull in the campaign details but also know which data types were selected.
How I originally had it set up was the data types were in 1 field, comma separated but learned is not ideal to do that.
What would be the best way to accomplish this? Storing the data as XML ?
UPDATE -
Here is an example of the query I was trying to get to work (its probably way off).
BEGIN
SET NOCOUNT ON;
BEGIN
SELECT *
FROM (SELECT A.[campaignID] as campaignID,
A.[campaignTitle],
A.[campaignDesc],
A.[campaignType],
A.[campaignStatus],
A.[duration],
A.[whoCreated],
B.[campaignID],
B.[dataType],
(SELECT *
FROM Tags_Campaign_Settings
WHERE campaignID = #campaignID) AS dataTypes
FROM Tags_Campaigns AS A
INNER JOIN
Tags_Campaign_Settings AS B
ON A.[campaignID] = B.[campaignID]
WHERE A.[campaignID] = #campaignID
) AS a
FOR XML PATH ('campaigns'), TYPE, ELEMENTS, ROOT ('root');
END
END
Create a join table called Campain_DataType with campaignId and dataTypeId. Make sure they're foreign key constrained to the respective tables. When you query for campaign data, you can either create a separate query to get the data type information based on the campaignId, or you can do a left outer join to fetch campaigns and their data types together.
If you want to collapse the 3 data types into the same row, then give the following a shot. It's definitely on the hacky side, and it'll only work with a fixed number of data types. If you add another data type, you'll have to update this query to support it.
SELECT
Campaign.ID,
Campaign.foo,
Campaign.bar,
dataType1.hasDataType1,
dataType2.hasDataType2,
dataType3.hasDataType3
FROM
Campaign
LEFT OUTER JOIN
( SELECT
1 as hasDataType1,
Campaign_DataType.campaignID
FROM
DataType
INNER JOIN Campaign_DataType ON Campaign_DataType.dataTypeId = DataType.id
WHERE
DataType.Type = 'Type1'
) dataType1 ON dataType1.campaignID = Campaign.ID
LEFT OUTER JOIN
( SELECT
1 as hasDataType2,
Campaign_DataType.campaignID
FROM
DataType
INNER JOIN Campaign_DataType ON Campaign_DataType.dataTypeId = DataType.id
WHERE
DataType.Type = 'Type2'
) dataType2 ON dataType2.campaignID = Campaign.ID
LEFT OUTER JOIN
( SELECT
1 as hasDataType3,
Campaign_DataType.campaignID
FROM
DataType
INNER JOIN Campaign_DataType ON Campaign_DataType.dataTypeId = DataType.id
WHERE
DataType.Type = 'Type3'
) dataType3 ON dataType3.campaignID = Campaign.ID
The record you receive for each Campaign will have three fields: hasDataType1, hasDataType2, hasDataType3. These columns will be 1 for yes, NULL for no.
Looks to me like what you want here is a crosstab query. Take a look at:
Sql Server 2008 Cross Tab Query