Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Triggers and Statistics in SQL Server #2313

Open
junior-paytech opened this issue Sep 25, 2024 · 2 comments
Open

Triggers and Statistics in SQL Server #2313

junior-paytech opened this issue Sep 25, 2024 · 2 comments

Comments

@junior-paytech
Copy link

I am working on a project where database has both triggers and statistics and when I try to drop trigger and after that drop statistics I got an error that I don't have permission or statistcs does not exists.

I am using "sa" user and the same sql script works when running it on SQL Express or other DBMS.

Does anybody have any suggestion?

I also created a "Base migration" that extends AbstractMigration to implement the methods to check for statistics

<?php

declare(strict_types=1);

namespace App\Infrastructure\Database\Migrations;

use Phinx\Migration\AbstractMigration;

abstract class BaseMigration extends AbstractMigration
{
    public function getStatistics(string $tableName, string $columnName)
    {
        return $this->query("SELECT
                    s.name AS statistics_name,
                    c.name AS column_name,
                    t.name AS table_name
                FROM sys.stats AS s
                JOIN sys.stats_columns AS sc ON s.stats_id = sc.stats_id AND s.object_id = sc.object_id
                JOIN sys.columns AS c ON sc.object_id = c.object_id AND sc.column_id = c.column_id
                JOIN sys.tables AS t ON s.object_id = t.object_id
                WHERE
                    t.name = '{$tableName}' AND
                    c.name = '{$columnName}'")->fetchAll(\PDO::FETCH_OBJ);
    }

    /**
     * Check if the informed table.column has statistics
     * @param string $tableName
     * @param string $columnName
     * @return bool
     */
    public function hasStatistic(string $tableName, string $columnName): bool
    {
        return count($this->getStatistics($tableName, $columnName)) > 0;
    }

    public function dropStatistic(string $tableName, string $columnName)
    {
        $return = true;
        $statistics = $this->getStatistics($tableName, $columnName);
        foreach ($statistics as $statistic) {
            if ($statistic->table_name === $tableName && $statistic->column_name === $columnName) {
                $this->query("DROP STATISTICS {$tableName}.{$columnName}")->execute();
            }
        }

        return $return;
    }

    public function createStatistic(string $tableName, string $columnName)
    {
        return $this->query("CREATE STATISTICS {$columnName} ON {$tableName}({$columnName})")->execute();
    }
}
@MasterOdin
Copy link
Member

Can you share a migration that creates a trigger and statistic, as well as the migration that has the issue?

@junior-paytech
Copy link
Author

Hi @MasterOdin , below the migration code.

I just changed some table / column names to "ofuscate" original data

<?php
declare(strict_types=1);
require 'BaseMigration.php';
final class UpdatePFWarningsRemoveRelatedId extends BaseMigration
{
    private $warningTable = 'PF_Warnings';
    private $logWarningsTable = 'TAU_PF_Warnings';
    public function up(): void
    {
        $this->dropTriggersIfExists();
        $this->dropRelatedIdColumn();
		$this->createTriggersWithoutRelatedId();
    }
    public function down(): void
    {
		$this->dropTriggersIfExists();
		
        $this->createRelatedIdColumn();
		
        $this->createTriggersWithRelatedId();
    }
	
	private function dropTriggersIfExists()
    {
        $this->query("
            IF OBJECT_ID('TAU_DEL_PF_Warnings', 'TR') IS NOT NULL
            BEGIN
                DROP TRIGGER TAU_DEL_PF_Warnings;
            END
        ")->execute();
        $this->query("
        IF OBJECT_ID('TAU_UPD_PF_Warnings', 'TR') IS NOT NULL
            BEGIN
                DROP TRIGGER TAU_UPD_PF_Warnings;
            END
        ")->execute();
    }
	
	private function createTriggersWithoutRelatedId()
    {
        $this->getAdapter()->getConnection()->exec("
         CREATE TRIGGER TAU_DEL_PF_Warnings
         ON PF_Warnings
         AFTER DELETE
         AS
         BEGIN
             SET NOCOUNT ON;
             BEGIN TRY
                 INSERT INTO TAU_PF_Warnings
                 (idaviso, idempresa, ntipo, idcliente, naviso, lativo, ndiasaviso, idusucad, dcadas, idusu, datual, tau_tipo)
                 SELECT
                     idaviso, idempresa, ntipo, idcliente, naviso, lativo, ndiasaviso, idusucad, dcadas, idusu, datual, 1
                 FROM DELETED;
             END TRY
             BEGIN CATCH
             END CATCH
         END;
        ");
        $this->getAdapter()->getConnection()->exec("
        CREATE TRIGGER TAU_UPD_PF_Warnings
        ON PF_Warnings
        AFTER UPDATE
        AS
        BEGIN
            SET NOCOUNT ON;
            BEGIN TRY
                INSERT INTO TAU_PF_Warnings
                (idaviso, idempresa, ntipo, idcliente, naviso, lativo, ndiasaviso, idusucad, dcadas, idusu, datual, tau_tipo)
                SELECT
                    idaviso, idempresa, ntipo, idcliente, naviso, lativo, ndiasaviso, idusucad, dcadas, idusu, datual, 2
                FROM DELETED;
            END TRY
            BEGIN CATCH
            END CATCH
        END;
        ");
    }
	
	private function createTriggersWithRelatedId()
    {
        $this->getAdapter()->getConnection()->exec("
         CREATE TRIGGER TAU_DEL_PF_Warnings
         ON PF_Warnings
         AFTER DELETE
         AS
         BEGIN
             SET NOCOUNT ON;
             BEGIN TRY
                 INSERT INTO TAU_PF_Warnings
                 (idaviso, idempresa, idrelacionado, ntipo, idcliente, naviso, lativo, ndiasaviso, idusucad, dcadas, idusu, datual, tau_tipo)
                 SELECT
                     idaviso, idempresa, idrelacionado, ntipo, idcliente, naviso, lativo, ndiasaviso, idusucad, dcadas, idusu, datual, 1
                 FROM DELETED;
             END TRY
             BEGIN CATCH
             END CATCH
         END;
        ");
        $this->getAdapter()->getConnection()->exec("
        CREATE TRIGGER TAU_UPD_PF_Warnings
        ON PF_Warnings
        AFTER UPDATE
        AS
        BEGIN
            SET NOCOUNT ON;
            BEGIN TRY
                INSERT INTO TAU_PF_Warnings
                (idaviso, idempresa, idrelacionado, ntipo, idcliente, naviso, lativo, ndiasaviso, idusucad, dcadas, idusu, datual, tau_tipo)
                SELECT
                    idaviso, idempresa, idrelacionado, ntipo, idcliente, naviso, lativo, ndiasaviso, idusucad, dcadas, idusu, datual, 2
                FROM DELETED;
            END TRY
            BEGIN CATCH
            END CATCH
        END;
        ");
    }
	
	private function createRelatedIdColumn()
    {
        if ($this->table($this->tableAviso)->hasColumn('idrelacionado') === false) {
            $this->table($this->tableAviso)->addColumn('idrelacionado', 'integer', [
                'null' => false,
                'default' => 0,
                'after' => 'ntipo',
            ])->save();
            if ($this->hasStatistic($this->tableAviso, 'idrelacionado') === false) {
                $this->createStatistic($this->tableAviso, 'idrelacionado');
            }
        }
    }
	
	private function dropRelatedIdColumn()
    {
        if ($this->table($this->tableAviso)->hasColumn('idrelacionado')) {
            if ($this->hasStatistic($this->tableAviso, 'idrelacionado')) {
                $this->dropStatistic($this->tableAviso, 'idrelacionado');
            }
            $this->table($this->tableAviso)->removeColumn('idrelacionado')->update();
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants