SAP query - BSEG and BKPF - sap-erp

I have two tables in SAP ERP, BSEG and BKPF, can we identify which transactions came from BSIS, BASS, BAUD, BSAD, BSIK or BSAK? If yes then how? which columns and filters should we use?

You can use the account type field (BSEG-KOART):
BSEG-KOART = 'S' => G/L postings, index tables are BSIS and BSAS
BSEG-KOART = 'D' => customer postings, index tables are BSID and BSAD
BSEG-KOART = 'K' => vendor postings, index tables are BSIK and BSAK.
Alternatively you can use the posting key field as well (BSEG-BSCHL), however it is a bit more complicated:
BSEG-BSCHL = '40' or '50' => G/L postings
BSEG-BSCHL = '01' - '09' or '11' - '19' => customer postings
BSEG-BSCHL = '21' - '29' or '31' - '39' => vendor postings

Related

SQL query needed for a complex structure

I have a tricky SQL query that needs to be built to get the highest priority rule based on customer session and geo IP data.
I attached the following tables: rule, rule_attribute, rule_attribute_value.
rule - table where all rules are stored
Click here to see a screenshot of the 'rule' table
rule_attribute - table where all rule attributes are stored
Click here to see a screenshot of the 'rule_attribute' table
rule_attribute_value - table where all rule attribute values are stored
Click here to see a screenshot of the 'rule_attribute_value' table
When the customer logs in, I have access to all those attributes (customer_id, customer_group_id, country_id, subdivision_one_id, subdivision_two_id). Only customer_id and customer_group_id will always have values. The others are optional, but there is a dependency between them. We can't have subdivisions without selecting first a country. We can have a second subdivision without selecting a country and then the first subdivision.
What I would like to get is the highest priority rule that matches the session data in the most optimized way. I have a solution that involves some coding, but I want to see if it's possible directly through SQL.
Here are some examples of session data arrays:
Array
(
[customer_id] => 2
[customer_group_id] => 1
[current_store_id] => 0
[country_id] => 15
[subdivision_one_id] => 224
[subdivision_two_id] =>
)
Array
(
[customer_id] => 2
[customer_group_id] => 1
[current_store_id] => 0
[country_id] => 15
[subdivision_one_id] =>
[subdivision_two_id] =>
)
Array
(
[customer_id] => 3
[customer_group_id] => 2
[current_store_id] => 0
[country_id] =>
[subdivision_one_id] =>
[subdivision_two_id] =>
)
Without a better understanding of the rules and data this is the best I can come up with. It is based on your first array example -
SELECT `r`.*
FROM `rule_attribute_value` `rav`
INNER JOIN `rule` `r`
ON `rav`.`rule_id` = `r`.`rule_id`
INNER JOIN `rule_attribute` `ra`
ON `rav`.`attribute_id` = `ra`.`attribute_id`
WHERE
(`rav`.`store_id` = 0 AND `ra`.`attribute_code` = 'customer' AND `rav`.`value` = 2) OR
(`rav`.`store_id` = 0 AND `ra`.`attribute_code` = 'customer_group' AND `rav`.`value` = 1) OR
(`rav`.`store_id` = 0 AND `ra`.`attribute_code` = 'country' AND `rav`.`value` = 15) OR
(`rav`.`store_id` = 0 AND `ra`.`attribute_code` = 'subdivision_one' AND `rav`.`value` = 224)
GROUP BY `r`.`rule_id`
HAVING COUNT(DISTINCT `rav`.`attribute_id`) = 4 /* 4 IS THE NUMBER OF ATTRIBUTES BEING QUERIED */
ORDER BY `r`.`position` ASC
LIMIT 1;

Trouble interpreting what is making my query so slow

I have a query that takes 15 seconds to get 350 results in a MySQL 5.6 Server and I am unable to diagnose why, I am still very new to database optimizing. U
The EXPLAIN visual does show some non-unique key lookups but each only says one 1 row look up.
The tabular EXPLAIN which I am not able to interpret and I am hoping someone else can here looks like .
I have tried switching the ending LIMIT = 350 to 100, 10, and the query takes exactly the same amount of time to run, about 15 seconds.
I have tried nixing the views but besides making it hard to recreate this query it did not improve performance.
Perhaps related, in other EXPLAIN statements in our MySQL DB, I've seen a view referenced with Materialized next to it, but that does not appear near next to any of the three views used in this query, in fact I don't even see the views referenced at all instead only the tables they reference. Is that a factor?
My last attempt was replacing the final selected column which is a listlineitems.* with the specific columns, since I've read that can improve speed and is just better practice, but I get the sense that is not going to dramatically improve this situation.
Here's the query -
SELECT
0 AS 'Check',
DATE_FORMAT(`listlineitems`.`dateEntered`,
'%Y-%m-%d') AS 'Date Entered',
`listlineitems`.`itemId` AS 'parentTableIdx',
`listlineitems`.`parentProjectId` AS 'parentProjectIdx',
`listlineitems`.`idx` AS 'ID',
IF(`listlineitems`.`active` = 1,
'Active',
'Inactive') AS 'Active/Inactive',
CONCAT(`listUsers`.`FirstName`,
' ',
`listUsers`.`LastName`) AS 'Employee',
CASE `listlineitems`.`type`
WHEN 1 THEN 'Time Entry'
WHEN 2 THEN 'Expense Entry'
END AS 'Type',
`listcustomers`.`name` AS 'Customer',
`listlocations`.`name` AS 'Location',
`listareas`.`name` AS 'Area',
`listassets`.`name` AS 'Asset',
`listprojects`.`name` AS 'Project',
`listprojects`.`number` 'Project #',
`listprojects`.`autoassign` 'autoassign',
`listactivities`.`name` AS 'Activity',
(CASE `listlineitems`.`type`
WHEN 1 THEN `listlineitems`.`qty`
WHEN 2 THEN `listlineitems`.`qty`
END) AS 'Quantity',
`listlineitems`.`taxable` AS 'Taxable',
`listlineitems`.`totalAmount` - `listlineitems`.`taxAmount` AS 'Pre-Tax Amount',
`listlineitems`.`taxAmount` AS 'Tax Amount',
`listlineitems`.`totalAmount` AS 'Total Amount',
`listCustomers`.`idx` AS 'parentCustomerIdx',
`listLocations`.`idx` AS 'parentLocationIdx',
`listAreas`.`idx` AS 'parentAreaIdx',
`listAssets`.`idx` AS 'parentAssetIdx',
CONCAT(`listcustomers`.`name`,
'/',
`listlocations`.`name`,
'/',
`listareas`.`name`,
'/',
`listassets`.`name`,
'/',
`listprojects`.`name`) AS 'Path',
IF(`listlineitems`.`customerViewable` = 1,
'Yes',
'No') AS 'Cust. Viewable',
(CASE
WHEN `listlineitems`.`type` = 2 THEN `listexpenseentry`.`TotalCostToPSI` - `listexpenseentry`.`TaxCostToPSI`
ELSE `listlineitems`.`totalAmount` - `listlineitems`.`taxAmount`
END) AS 'preTaxCostPSI',
(CASE
WHEN `listlineitems`.`type` = 2 THEN `listexpenseentry`.`TaxCostToPSI`
ELSE `listlineitems`.`taxAmount`
END) AS 'taxCostPSI',
(CASE
WHEN `listlineitems`.`type` = 2 THEN `listexpenseentry`.`TotalCostToPSI`
ELSE `listlineitems`.`totalAmount`
END) AS 'totalCostPSI',
view_solinx2.lastAltered AS 'lastalteredSO',
view_polinx2.lastAlteredPO AS 'lastalteredPO',
view_invlinx2.lastAlteredInv AS 'lastalteredInv',
view_solinx2.lastAlteredAfterConfirmation AS 'lastAlteredAfterConfirmation',
view_solinx2.roleIdSO AS 'roleIdSO',
view_polinx2.roleIdPO AS 'roleIdPO',
view_polinx2.userIdPO AS 'userIdPO',
view_polinx2.lastAlteredafterConfirmation AS 'lastAlteredAfterConfirmationPO',
view_invlinx2.roleIdInv AS 'roleIdInv',
view_invlinx2.userIdInv AS 'userIdInv',
view_invlinx2.lastAlteredafterConfirmation AS 'lastAlteredAfterConfirmationInv',
view_solinx2.roleId AS 'roleId',
view_solinx2.userId AS 'userId',
view_solinx2.soId AS 'SOId',
view_solinx2.autoassignSO AS 'autoassignSO',
IF(view_solinx2.notNeeded = 1,
'Not Needed',
view_solinx2.number) AS 'SOname',
view_solinx2.dateEntered AS 'SoDate',
view_solinx2.totalSOAmount AS 'SoTotal',
view_invlinx2.invId AS 'InvId',
IF(view_solinx2.notNeeded = 1,
'------',
view_invlinx2.`number`) AS 'InvName',
view_invlinx2.dateEntered AS 'InvDate',
view_invlinx2.amount AS 'InvTotal',
view_polinx2.poId AS 'POId',
IF(view_solinx2.notNeeded = 1,
'------',
view_polinx2.`number`) AS 'POName',
view_polinx2.dateEntered AS 'PODate',
view_polinx2.amount AS 'POTotal',
(SELECT
listsalesorders.number
FROM
listsalesorders
WHERE
listsalesorders.idx = autoassign) AS 'test',
`listlineitems`.*
FROM
`listlineitems`
LEFT JOIN
`listUsers` ON `listlineitems`.`individualId` = `listUsers`.`idx`
LEFT JOIN
`listprojects` ON `listlineitems`.`parentProjectId` = `listprojects`.`idx`
LEFT JOIN
`listassets` ON `listlineitems`.`parentAssetId` = `listassets`.`idx`
LEFT JOIN
`listareas` ON `listlineitems`.`parentAreaId` = `listareas`.`idx`
LEFT JOIN
`listlocations` ON `listlineitems`.`parentLocationId` = `listlocations`.`idx`
LEFT JOIN
`listcustomers` ON `listlineitems`.`parentCustomerId` = `listcustomers`.`idx`
LEFT JOIN
`listactivities` ON `listactivities`.`idx` = `listlineitems`.`activityCode`
LEFT JOIN
`listexpenseentry` ON (`listexpenseentry`.`idx` = `listlineitems`.`itemId`
AND `listlineitems`.`type` = 2)
LEFT JOIN
view_solinx2 ON view_solinx2.idx = listlineitems.idx
LEFT JOIN
view_polinx2 ON view_polinx2.idx = listlineitems.idx
LEFT JOIN
view_invlinx2 ON view_invlinx2.idx = listlineitems.idx
GROUP BY `listlineitems`.`idx`
ORDER BY `listlineitems`.`dateEntered` DESC
LIMIT 10;
I am at a loss as to what else I can do to improve this and any suggestions are very much appreciated.
You are selecting everything from listlineitems table (100+ K records), joining many tables, then grouping by idx and then throwing out most results.
You can:
Try to add unique index (dateEntered, idx) to listlineitems
Try limit listlineitems by dateEntered if acceptable (WHERE dateEntered > DATE_SUB(NOW(), INTERVAL 30 DAYS)). dateEntered must be indexed
Try to put select from listlineitems + grouping + limit into subquery so MySQL will do joins to only these 10 rows returned by subquery.
Convert dependent subquery (listsalesorders) to left join

CakePHP 3 - finder not loading correct data

I have a table called Bookings, with the following attributes:
id
artist_id - foreign key
status
amount
created
modified
It is associated with tables Artists, Payments and Sessions.
In the View, I have used the jQuery plugin DataTables to display Bookings which meet the following conditions:
Their status is equal to 'confirmed'
Associated table Session's attribute date_end must be greater than the current date.
However, the second condition does not seem to work in that it returns nothing when data entries which do meet that condition exist.
In my controller, the corresponding find for this particular table:
$bookingsConfirmed = $this->Bookings->find('all',[
'contain' => ['Sessions', 'Sessions.Studios', 'Sessions.Engineers', 'Artists'],
'conditions'=>['status' => 'confirmed', 'Sessions.date_end >=' => DATE(Time::now())],
'order'=>['Bookings.created'=>'ASC']
]);
I've attempted the following changes to the condition, all of which showed no notable difference:
date_end >=' => DATE(Time::now())
'Sessions.date_end >=' => Time::now()
'date(Sessions.date_end) >=' => DATE(Time::now())
'date(Sessions.date_end) >=' => date(DATE(Time::now()))
If I switch around the >= sign to a <, all my booking data appear, despite not all of them meeting that condition.
In the SQL log, this is the output for this particular table:
SELECT
Bookings.id AS `Bookings__id`,
Bookings.artist_id AS `Bookings__artist_id`,
Bookings.status AS `Bookings__status`,
Bookings.amount AS `Bookings__amount`,
Bookings.created AS `Bookings__created`,
Bookings.modified AS `Bookings__modified`,
Sessions.id AS `Sessions__id`,
Sessions.booking_id AS `Sessions__booking_id`,
Sessions.studio_id AS `Sessions__studio_id`,
Sessions.engineer_id AS `Sessions__engineer_id`,
Sessions.guestengineer_id AS `Sessions__guestengineer_id`,
Sessions.date_start AS `Sessions__date_start`,
Sessions.date_end AS `Sessions__date_end`,
Sessions.starttime AS `Sessions__starttime`,
Sessions.hours AS `Sessions__hours`,
Sessions.session_genre AS `Sessions__session_genre`,
Sessions.no_people AS `Sessions__no_people`,
Sessions.studio_usage AS `Sessions__studio_usage`,
Sessions.otherpeople_req AS `Sessions__otherpeople_req`,
Sessions.special_req AS `Sessions__special_req`,
Sessions.created AS `Sessions__created`,
Sessions.modified AS `Sessions__modified`,
Studios.id AS `Studios__id`,
Studios.name AS `Studios__name`,
Studios.description AS `Studios__description`,
Studios.created AS `Studios__created`,
Studios.modified AS `Studios__modified`,
Engineers.id AS `Engineers__id`,
Engineers.user_id AS `Engineers__user_id`,
Engineers.eng_firstname AS `Engineers__eng_firstname`,
Engineers.eng_lastname AS `Engineers__eng_lastname`,
Engineers.eng_email AS `Engineers__eng_email`,
Engineers.eng_phoneno AS `Engineers__eng_phoneno`,
Engineers.eng_status AS `Engineers__eng_status`,
Engineers.rate AS `Engineers__rate`,
Engineers.created AS `Engineers__created`,
Engineers.modified AS `Engineers__modified`,
Artists.id AS `Artists__id`,
Artists.name AS `Artists__name`,
Artists.cp_id AS `Artists__cp_id`,
Artists.user_id AS `Artists__user_id`,
Artists.genre AS `Artists__genre`,
Artists.created AS `Artists__created`,
Artists.modified AS `Artists__modified`
FROM
bookings Bookings
LEFT JOIN sessions Sessions ON Bookings.id = (Sessions.booking_id)
INNER JOIN studios Studios ON Studios.id = (Sessions.studio_id)
LEFT JOIN engineers Engineers ON Engineers.id = (Sessions.engineer_id)
INNER JOIN artists Artists ON Artists.id = (Bookings.artist_id)
WHERE
(
status = 'confirmed'
AND Sessions.date_end >= '20/2/17, 4:47 p02'
)
ORDER BY
Bookings.created ASC
As of today's date, there should be 3 entries that show. I did a print of Time::now and got the date:
Cake\I18n\Time Object
(
[time] => 2017-02-20T16:47:11+11:00
[timezone] =>
[fixedNowTime] =>
)
The odd thing is that this was working fine last week, and the submission forms still work. In MySQL for example, my latest entry that I inputted today shows Sessions.date_end filled in, in a YYYY-MM-DD format like all the entries that proceeded it.
Try this line instead
`date_end >=' => date('Y-m-d H:i:s', Time::now()->getTimestamp())`
Note : use the exact word case for functions, date instead of DATE

How to use the table columns instead of variables in QueryExpression::addCase()

In CakePHPs new ORM, you can use the QueryBuilder to build (in theory) any query.
I want to select the value of one of two columns, depending on another value. In a regular query, that can be done as follows:
SELECT IF(from_id = 1, to_id, from_id) AS other_id FROM messages;
I am trying to archive the same query using the QueryBuilder and QueryExpression::addCase()
$messagesQuery = $this->Messages->find('all');
$messagesQuery->select([
'other_id' => $messagesQuery->newExpr()->addCase(
$messagesQuery->newExpr()->add(['from_id' => $this->authUser->id]),
['to_id', 'from_id'],
['integer', 'integer']
)
]);
This does not work, as the passed values are not integers, but rather table columns containing integers.
Through trial and error (using the method add() again), I got the following:
$messagesQuery = $this->Messages->find('all');
$messagesQuery->select([
'other_id' => $messagesQuery->newExpr()->addCase(
$messagesQuery->newExpr()->add(['from_id' => $this->authUser->id]),
[
$messagesQuery->newExpr()->add(['to_id']),
$messagesQuery->newExpr()->add(['from_id'])
],
['integer', 'integer']
)
]);
This results in the following query:
SELECT (CASE WHEN from_id = 1 THEN to_id END) AS `other_id` FROM messages Messages
Now, the ELSE part is missing, although the CakePHP book states:
Any time there are fewer case conditions than values, addCase will automatically produce an if .. then .. else statement.
The examples in the CakePHP book are not very helpful in this case, as they only use static integers or strings as values, for example:
#SELECT SUM(CASE published = 'Y' THEN 1 ELSE 0) AS number_published, SUM(CASE published = 'N' THEN 1 ELSE 0) AS number_unpublished FROM articles GROUP BY published
$query = $articles->find();
$publishedCase = $query->newExpr()->addCase($query->newExpr()->add(['published' => 'Y']), 1, 'integer');
$notPublishedCase = $query->newExpr()->addCase($query->newExpr()->add(['published' => 'N']), 1, 'integer');
$query->select([
'number_published' => $query->func()->sum($publishedCase),
'number_unpublished' => $query->func()->sum($unpublishedCase)
])
->group('published');
Is there a way to get the method addCase to use the two table columns as values instead of just static values?
As it turns out, I was just one logical step short of the solution in my previous edit.
As the CakePHP book correctly states:
Any time there are fewer case conditions than values, addCase will automatically produce an if .. then .. else statement.
For that to work though, both the conditions and values have to be an array, even if there is only one condition. (This the CakePHP book does not state.)
This code:
$messagesQuery = $this->Messages->find('all');
$messagesQuery->select([
'other_id' => $messagesQuery->newExpr()->addCase(
[
$messagesQuery->newExpr()->add(['from_id' => $this->authUser->id])
],
[
$messagesQuery->newExpr()->add(['to_id']),
$messagesQuery->newExpr()->add(['from_id'])
],
['integer', 'integer']
)
]);
results in this query:
SELECT (CASE WHEN from_id = 1 THEN to_id ELSE from_id END) AS `other_id` FROM messages Messages
Eureka

Eloquent query building complex query to get unique records searching for an ID in 2 different columns in same table

I'm migrating a project to Laravel 4 and I am stuck with a quite complex query, which I'd like to migrate into a proper Eloquent query.
I have a table that contains chat messages, called chat_messages with a representing Model Chatmessage
The table contains a sender and a receipient column with a user id linking to the users table and User Model.
The query to get a list with all user IDs of all chat partners in raw SQL on the old version of the application is as follows:
$sql_allChatPartners = "SELECT DISTINCT chatPartner
FROM ( SELECT * FROM (
SELECT cm_receipient AS chatPartner, cm_sent_at
FROM chat_messages WHERE cm_sender = '".$me->userID."'
UNION
SELECT cm_sender AS chatPartner, cm_sent_at
FROM chat_messages WHERE cm_receipient = '".$me->userID."'
) whateva ORDER BY whateva.cm_sent_at DESC ) other";
Sorry for naming the "fake" tables whateva and other :-)
Could anyone put me in the right direction to do this with Eloquent Querybuilder?
It is important that I get the list of chatPartner IDs in the correct order, where the last chat message has been exchanged as first chatPartner. And the chatPartner where longest inactivity was in the chat as last entry.
This is what I got so far in my User Model...
public function allopenchats(){
$asSender = Chatmessage::where('sender', $this->id)->select('receipient as chatPartner, created_at');
$asBoth = Chatmessage::where('receipient', $this->id)->select('sender as chatPartner, created_at')
->union($asSender)->orderBy('created_at', 'desc')->get();
}
I renamed the columns cm_receipient to receipient, cm_sender to sender and sent_at to created_at in the new database for the new version
Your help would be very much appreciated!
You sql may change to:
SELECT IF (cm_receipient = '10', cm_sender, IF (cm_sender = '10',cm_receipient, 'no')) AS chatPartner, cm_sent_at
FROM chat_messages
WHERE cm_receipient = '10' OR cm_sender = '10'
GROUP BY chatPartner
HAVING chatPartner != 'no'
order by cm_sent_at DESC
In orm:
Chatmessage::where('sender','=',$this->id)
->orWhere('receipient','=',$this->id)
->select(DB::raw('IF (receipient = '.$this->id.', sender, IF (sender = '.$this->id.',receipient, 'no' )) AS chatPartner'), 'created_at')
->groupBy('chatPartner')
->having('chatPartner', '!=', 'no')
->orderBy('created_at', 'desc')
->get();
Thanks very much to Vitalik_74, I wouldn't have come that far without him.
Here is now the final query, although its not in ORM, but it is working fine and gives me the result I need.
$result = DB::select("SELECT *
FROM (
SELECT IF( receipient = '".$this->id."', sender, IF( sender = '".$this->id."', receipient, 'no' ) ) AS chatPartner, created_at
FROM chatmessages
WHERE receipient = '".$this->id."'
OR sender = '".$this->id."'
HAVING chatPartner != 'no'
ORDER BY created_at DESC
)whateva
GROUP BY whateva.chatPartner
ORDER BY whateva.created_at DESC");
if there is someone out there who can do this query with the Laravel Query Builder, I would be happy to see it. For now I'll leave it like this.