Count en tablas relacionadas con MANY_MANY

Hola,

Tengo 2 tablas relacionadas mediante otra tabla.

Demarcaciones -> rel_demarcaciones_municipios <- Municipios

Relación Demarcaciones:




	public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

			'municipios' => array(self::MANY_MANY, 'Municipios', 'rel_demarcaciones_municipios(id_demarcacion, id_municipio)','alias'=>'municipios'),

		);

	}



El caso es que hago la siguiente consulta:




$demarcaciones=Demarcaciones::model()->with(array('municipios'))->together()->findAll($criteria);



Y me devuelve los datos que necesito.

Como estoy haciendo un paginador, necesito saber el COUNT total y hago lo siguiente:




$count=Demarcaciones::model()->with(array('municipios'))->count();



Esta última no me da lo correcto ya que hace la siguiente consulta:




SELECT COUNT(DISTINCT `demarcaciones`.`id`) FROM `demarcaciones`  LEFT OUTER JOIN `rel_demarcaciones_municipios` municipios_municipios ON (`demarcaciones`.`id`=municipios_municipios.`id_demarcacion`) LEFT OUTER JOIN `municipios` municipios ON (municipios.`id`=municipios_municipios.`id_municipio`)



Y yo quiero que me haga un COUNT(*).

Imagino que es por las relaciones entre las tablas, pero ¿hay alguna manera de forzar a COUNT(*)?

Saludos.

Try if




$count=Demarcaciones::model()->with(array('municipios'=>array('select'=>'COUNT(*)')))->count();



works???

No…

Da la siguiente excepción:

[html]

<h1>CDbException</h1>

<h3>Descripción</h3>

<p class="message">

Active record &quot;Municipios&quot; esta intentando de seleccionar una columna inválida &quot;COUNT(*)&quot;. Nota: La columna puede existir en la base o ser una expresion con alias.</p>

<h3>Archivo Fuente:</h3>

<p>

C:\Servers\htdocs\yii\framework\db\ar\CActiveFinder.php(837)</p>

<div class="source">

<pre>

00825: }

00826: else if(preg_match(’/^(.*?)\s+AS\s+(\w+)$/i’,$name,$matches)) // if the column is already aliased

00827: {

00828: $alias=$matches[2];

00829: if(!isset($this-&gt;_columnAliases[$alias]) || $this-&gt;_columnAliases[$alias]!==$alias)

00830: {

00831: $this-&gt;_columnAliases[$alias]=$alias;

00832: $columns[]=$name;

00833: $selected[$alias]=1;

00834: }

00835: }

00836: else

<div class=“error”>00837: throw new CDbException(Yii::t(‘yii’,‘Active record &quot;{class}&quot; is trying to select an invalid column &quot;{column}&quot;. Note, the column must exist in the table or be an expression with alias.’,

</div>00838: array(’{class}’=&gt;get_class($this-&gt;model), ‘{column}’=&gt;$name)));

[/html]

Y esto?




$count=Demarcaciones::model()->with('municipios')->together()->count();



nada…

Lo había probado ya para que hiciese las JOINs en la misma consulta y el resultado es el mismo.




SELECT COUNT(DISTINCT `demarcaciones`.`id`) FROM `demarcaciones`  LEFT OUTER JOIN `rel_demarcaciones_municipios` municipios_municipios ON (`demarcaciones`.`id`=municipios_municipios.`id_demarcacion`) LEFT OUTER JOIN `municipios` municipios ON (municipios.`id`=municipios_municipios.`id_municipio`)



prueba hacer esto

define una variable "cantidad" en el modelo

y luego intenta hacer lo que dice PoL




$count=Demarcaciones::model()->with(array('municipios'=>array('select'=>'COUNT(*) as cantidad')))->count();

o prueba lo siguiente (siempre con "cantidad" en el modelo )




$criteria->select='count(*) as cantidad'


$count=Demarcaciones::model()->with(array('municipios'))->together()->find($criteria)->queryScalar;

Saludos

Buenas de nuevo,

He añadido en el modelo la variable $cantidad a nivel global de la clase de esta manera:




class Demarcaciones extends CActiveRecord

{

	/**

	 * The followings are the available columns in table 'demarcaciones':

	 * @var integer $id

	 * @var string $referencia

	 * @var string $denominacion

	 * @var string $canal_multiple

	 * @var string $potencia_maxima

	 * @var string $superficie_total

	 * @var string $densidad_poblacion

	 * @var string $observaciones

	 */


         var $cantidad=0;


	/**

	 * Returns the static model of the specified AR class.

	 * @return CActiveRecord the static model class

	 */

	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}


	/**

	 * @return string the associated database table name

	 */

	public function tableName()

	{

		return 'demarcaciones';

	}


	/**

	 * @return array validation rules for model attributes.

	 */

	public function rules()

	{

		return array(

			array('referencia','length','max'=>10),

			array('denominacion','length','max'=>64),

			array('canal_multiple','length','max'=>3),

			array('potencia_maxima','length','max'=>6),

			array('superficie_total','length','max'=>45),

			array('densidad_poblacion','length','max'=>45),

			array('referencia, denominacion, canal_multiple, potencia_maxima, superficie_total, densidad_poblacion', 'required'),

		);

	}


	/**

	 * @return array relational rules.

	 */

	public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

			'municipios' => array(self::MANY_MANY, 'Municipios', 'rel_demarcaciones_municipios(id_demarcacion, id_municipio)','alias'=>'municipios'),

		);

	}


	/**

	 * @return array customized attribute labels (name=>label)

	 */

	public function attributeLabels()

	{

		return array(

			'id' => 'Id',

			'referencia' => 'Referencia',

			'denominacion' => 'Denominacion',

			'canal_multiple' => 'Canal Multiple',

			'potencia_maxima' => 'Potencia Maxima',

			'superficie_total' => 'Superficie Total',

			'densidad_poblacion' => 'Densidad Poblacion',

			'observaciones' => 'Observaciones',

		);

	}

}



Lo primero que me has comentado me devuelve la consulta de siempre como si no hiciese caso al criterio puesto:




Querying SQL: SELECT COUNT(DISTINCT `demarcaciones`.`id`) FROM `demarcaciones`  LEFT OUTER JOIN `rel_demarcaciones_municipios` municipios_municipios ON (`demarcaciones`.`id`=municipios_municipios.`id_demarcacion`) LEFT OUTER JOIN `municipios` municipios ON (municipios.`id`=municipios_municipios.`id_municipio`)



Y la segunda opción me devuelve esto:




CException

Descripción


Propiedad "Demarcaciones"."queryScalar" no se encuentra definida.

Archivo Fuente:


C:\Servers\htdocs\yii\framework\db\ar\CActiveRecord.php(434)


00422:      */

00423:     public function __get($name)

00424:     {

00425:         if(isset($this->_attributes[$name]))

00426:             return $this->_attributes[$name];

00427:         else if(isset($this->getMetaData()->columns[$name]))

00428:             return null;

00429:         else if(isset($this->_related[$name]))

00430:             return $this->_related[$name];

00431:         else if(isset($this->getMetaData()->relations[$name]))

00432:             return $this->getRelated($name);

00433:         else

00434: return parent::__get($name);

00435:     }

00436: 

00437:     /**

00438:      * PHP setter magic method.

00439:      * This method is overridden so that AR attributes can be accessed like properties.

00440:      * @param string property name

00441:      * @param mixed property value

00442:      */

00443:     public function __set($name,$value)

00444:     {

00445:         if($this->setAttribute($name,$value)===false)

00446:         {


Stack Trace


#0 C:\Servers\htdocs\yii\framework\db\ar\CActiveRecord.php(434): CComponent->__get('queryScalar')

#1 C:\Servers\htdocs\aurora\protected\controllers\InfoGestionTdtController.php(220): CActiveRecord->__get('queryScalar')

#2 C:\Servers\htdocs\yii\framework\web\actions\CInlineAction.php(32): InfoGestionTdtController->actionCargarDemarcaciones()

#3 C:\Servers\htdocs\yii\framework\web\CController.php(300): CInlineAction->run()

#4 C:\Servers\htdocs\yii\framework\web\filters\CFilterChain.php(129): CController->runAction(Object(CInlineAction))

#5 C:\Servers\htdocs\yii\framework\web\filters\CFilter.php(41): CFilterChain->run()

#6 C:\Servers\htdocs\yii\framework\web\CController.php(957): CFilter->filter(Object(CFilterChain))

#7 C:\Servers\htdocs\yii\framework\web\filters\CInlineFilter.php(59): CController->filterAccessControl(Object(CFilterChain))

#8 C:\Servers\htdocs\yii\framework\web\filters\CFilterChain.php(126): CInlineFilter->filter(Object(CFilterChain))

#9 C:\Servers\htdocs\yii\framework\web\CController.php(283): CFilterChain->run()

#10 C:\Servers\htdocs\yii\framework\web\CController.php(257): CController->runActionWithFilters(Object(CInlineAction), Array)

#11 C:\Servers\htdocs\yii\framework\web\CWebApplication.php(310): CController->run('cargarDemarcaci...')

#12 C:\Servers\htdocs\yii\framework\web\CWebApplication.php(120): CWebApplication->runController('infoGestionTdt/...')

#13 C:\Servers\htdocs\yii\framework\base\CApplication.php(135): CWebApplication->processRequest()

#14 C:\Servers\htdocs\aurora\index.php(13): CApplication->run()

#15 {main}



Pero también veo que llega a realizar a preparar la consulta:




Querying SQL: SELECT count(*) as cantidad, `demarcaciones`.`id` AS `t0_c0`, municipios.`id` AS `t1_c0`, municipios.`nombre` AS `t1_c1`, municipios.`id_provincia` AS `t1_c2` FROM `demarcaciones`  LEFT OUTER JOIN `rel_demarcaciones_municipios` municipios_municipios ON (`demarcaciones`.`id`=municipios_municipios.`id_demarcacion`) LEFT OUTER JOIN `municipios` municipios ON (municipios.`id`=municipios_municipios.`id_municipio`)



De todas formas lo que no comprendo es porque me pone el COUNT DISTINCT por defecto al usar el count normal… Imagino que será cosa por usar una tabla MANY_MANY de relaciones entre dos tablas.

Saludos.

Está última consulta si me devuelve por lo menos bien las filas, son 143 en el alias cantidad. Aunque da error del Yii.

Haciendo esto ya me funciona:




        $criteria2 = new CDbCriteria;

        $criteria2->select='count(*) as cantidad';


        $count=Demarcaciones::model()->with(array('municipios'))->together()->find($criteria2);

        

        $count=$count->cantidad;



Saludos y gracias.

PD: Todavía me queda la duda de el porque mete el DISTINC pero bueno a ver si investigo.

creo que debes definir cantidad como public, pero si funciona asi… dejalo

y si da error queryScalar

prueba asi




$criteria->select='count(*) as cantidad'


$cant_array=Demarcaciones::model()->with(array('municipios'))->together()->findAll($criteria);



y luego(si no da error) haz algo como print_r($cant_array) o var_dump($cant_array) para ver que contiene

supongo que la cantidad será $cant_array[0]

ya estamos cerca!!!

excelente !!

yo he tenido problemas con el count cuando la consulta no es "simple", he creado un post en bugs y un ticket en google code, pero no se ha resuelto ( Bug )

por ahora la solucion que me ha funcionado es la que te habia propuesto, pero queryScalar quizas no funcione con together

genera un ticket con esto, seguramente The Master algo hará

Saludos

Ok, eso haré.

Lo único que no me gusta de la consulta es que aparte de mostrarme el COUNT(*) en verdad se trae registros con la consiguiente pérdida de rendimiento…




Querying SQL: SELECT count(*) as cantidad, `demarcaciones`.`id` AS `t0_c0`, municipios.`id` AS `t1_c0`, municipios.`nombre` AS `t1_c1`, municipios.`id_provincia` AS `t1_c2` FROM `demarcaciones`  LEFT OUTER JOIN `rel_demarcaciones_municipios` municipios_municipios ON (`demarcaciones`.`id`=municipios_municipios.`id_demarcacion`) LEFT OUTER JOIN `municipios` municipios ON (municipios.`id`=municipios_municipios.`id_municipio`)



Todo esto viene porque uso el JQGrid y para poder paginar necesito saber el número total de registros. El código entero de la acción es esta:




    public function actionCargarDemarcaciones()

    {

        $page = $_GET['page']; // get the requested page

        $limit = $_GET['rows']; // get how many rows we want to have into the grid

        $sidx = $_GET['sidx']; // get index row - i.e. user click to sort

        $sord = $_GET['sord']; // get the direction

        $start = $limit*$page - $limit; // do not put $limit*($page - 1)


        if(!$sidx) $sidx =1;


        // connect to the database

        $criteria = new CDbCriteria;

        $criteria->limit = $limit;

        $criteria->offset = $start;

        $criteria->order = $sidx." ".$sord;

        

        $and="";

        

        

        if (isset($_GET['demarcaciones_referencia'])) {

            $criteria->condition = $criteria->condition." ".$and." demarcaciones.referencia LIKE ('%".$_GET['demarcaciones_referencia']."%')";

            $and="AND";

        }

        if (isset($_GET['demarcaciones_denominacion'])) {

            $criteria->condition = $criteria->condition." ".$and." demarcaciones.denominacion LIKE ('%".$_GET['demarcaciones_denominacion']."%')";

            $and="AND";

        }

        if (isset($_GET['demarcaciones_canal_multiple'])) {

            $criteria->condition = $criteria->condition." ".$and." demarcaciones.canal_multiple LIKE ('%".$_GET['demarcaciones_canal_multiple']."%')";

            $and="AND";

        }

        if (isset($_GET['demarcaciones_potencia_maxima'])) {

            $criteria->condition = $criteria->condition." ".$and." demarcaciones.potencia_maxima LIKE ('%".$_GET['demarcaciones_potencia_maxima']."%')";

            $and="AND";

        }

        if (isset($_GET['demarcaciones_superficie_total'])) {

            $criteria->condition = $criteria->condition." ".$and." demarcaciones.superficie_total LIKE ('%".$_GET['demarcaciones_potencia_maxima']."%')";

            $and="AND";

        }

        if (isset($_GET['demarcaciones_densidad_poblacion'])) {

            $criteria->condition = $criteria->condition." ".$and." demarcaciones.densidad_poblacion LIKE ('%".$_GET['demarcaciones_densidad_poblacion']."%')";

            $and="AND";

        }

        if (isset($_GET['municipios_nombre'])) {

            $criteria->condition = $criteria->condition." ".$and." municipios.nombre LIKE ('%".$_GET['municipios_nombre']."%')";

            $and="AND";

        }

        

        

        $demarcaciones=Demarcaciones::model()->with(array('municipios'))->together()->findAll($criteria);

       

        $criteria2 = new CDbCriteria;

        $criteria2->select='count(*) as cantidad';

        $criteria2->condition=$criteria->condition;


        $count=Demarcaciones::model()->with(array('municipios'))->together()->find($criteria2);

        

        $count=$count->cantidad;


        if( $count >0 ) {

                $total_pages = ceil($count/$limit);

        } else {

                $total_pages = 0;

        }

        if ($page > $total_pages) $page=$total_pages;


        $responce=new JqGrid();


        $responce->page = $page;

        $responce->total = $total_pages;

        $responce->records = $count;

        $i=0;


        foreach($demarcaciones as $n=>$demarcacion) {

        	foreach($demarcacion->municipios as $n2=>$municipio) {

                $responce->rows[$i]['id']=CHtml::encode($i);

                $responce->rows[$i]['cell']=array(

                    CHtml::encode($demarcacion->id),

                    CHtml::encode($demarcacion->referencia),

                    CHtml::encode($demarcacion->denominacion),

                    CHtml::encode($demarcacion->canal_multiple),

                    CHtml::encode($demarcacion->potencia_maxima),

                    CHtml::encode($demarcacion->superficie_total),

                    CHtml::encode($demarcacion->densidad_poblacion),

                    CHtml::encode($municipio->nombre),

                );

                $i++;

       		}

        }


        echo json_encode($responce);


    }



Muchas gracias a ambos por las respuestas.