mirror of
https://gitee.com/johng/gf.git
synced 2024-12-02 12:17:53 +08:00
fix issue #1292
This commit is contained in:
parent
2970864158
commit
9df860a202
@ -358,11 +358,11 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
|
|||||||
// parameter.
|
// parameter.
|
||||||
// See the example or unit testing cases for clear understanding for this function.
|
// See the example or unit testing cases for clear understanding for this function.
|
||||||
func (m *Model) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) {
|
func (m *Model) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) {
|
||||||
all, err := m.All()
|
result, err := m.All()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return all.ScanList(listPointer, attributeName, relation...)
|
return doScanList(m, result, listPointer, attributeName, relation...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count does "SELECT COUNT(x) FROM ..." statement for the model.
|
// Count does "SELECT COUNT(x) FROM ..." statement for the model.
|
||||||
|
@ -166,6 +166,10 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
|||||||
// doWithScanStructs handles model association operations feature for struct slice.
|
// doWithScanStructs handles model association operations feature for struct slice.
|
||||||
// Also see doWithScanStruct.
|
// Also see doWithScanStruct.
|
||||||
func (m *Model) doWithScanStructs(pointer interface{}) error {
|
func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||||
|
if v, ok := pointer.(reflect.Value); ok {
|
||||||
|
pointer = v.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
allowedTypeStrArray = make([]string, 0)
|
allowedTypeStrArray = make([]string, 0)
|
||||||
|
@ -42,21 +42,21 @@ import (
|
|||||||
//
|
//
|
||||||
// See the example or unit testing cases for clear understanding for this function.
|
// See the example or unit testing cases for clear understanding for this function.
|
||||||
func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) {
|
func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) {
|
||||||
if r.IsEmpty() {
|
return doScanList(nil, r, listPointer, bindToAttrName, relationKV...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// doScanList converts `result` to struct slice which contains other complex struct attributes recursively.
|
||||||
|
// The parameter `model` is used for recursively scanning purpose, which means, it can scans the attribute struct/structs recursively but
|
||||||
|
// it needs the Model for database accessing.
|
||||||
|
// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct.
|
||||||
|
func doScanList(model *Model, result Result, listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) {
|
||||||
|
if result.IsEmpty() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Necessary checks for parameters.
|
// Necessary checks for parameters.
|
||||||
if bindToAttrName == "" {
|
if bindToAttrName == "" {
|
||||||
return gerror.New(`bindToAttrName should not be empty`)
|
return gerror.New(`bindToAttrName should not be empty`)
|
||||||
}
|
}
|
||||||
//if len(relation) > 0 {
|
|
||||||
// if len(relation) < 2 {
|
|
||||||
// return gerror.New(`relation name and key should are both necessary`)
|
|
||||||
// }
|
|
||||||
// if relation[0] == "" || relation[1] == "" {
|
|
||||||
// return gerror.New(`relation name and key should not be empty`)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
reflectValue = reflect.ValueOf(listPointer)
|
reflectValue = reflect.ValueOf(listPointer)
|
||||||
@ -67,14 +67,14 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
|||||||
reflectKind = reflectValue.Kind()
|
reflectKind = reflectValue.Kind()
|
||||||
}
|
}
|
||||||
if reflectKind != reflect.Ptr {
|
if reflectKind != reflect.Ptr {
|
||||||
return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
return gerror.Newf("listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||||
}
|
}
|
||||||
reflectValue = reflectValue.Elem()
|
reflectValue = reflectValue.Elem()
|
||||||
reflectKind = reflectValue.Kind()
|
reflectKind = reflectValue.Kind()
|
||||||
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
|
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
|
||||||
return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
return gerror.Newf("listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||||
}
|
}
|
||||||
length := len(r)
|
length := len(result)
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
// The pointed slice is not empty.
|
// The pointed slice is not empty.
|
||||||
if reflectValue.Len() > 0 {
|
if reflectValue.Len() > 0 {
|
||||||
@ -134,7 +134,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
|||||||
// uid:UserId
|
// uid:UserId
|
||||||
relationResultFieldName = array[0]
|
relationResultFieldName = array[0]
|
||||||
relationBindToSubAttrName = array[1]
|
relationBindToSubAttrName = array[1]
|
||||||
if key, _ := gutil.MapPossibleItemByKey(r[0].Map(), relationResultFieldName); key == "" {
|
if key, _ := gutil.MapPossibleItemByKey(result[0].Map(), relationResultFieldName); key == "" {
|
||||||
return gerror.Newf(
|
return gerror.Newf(
|
||||||
`cannot find possible related table field name "%s" from given relation key "%s"`,
|
`cannot find possible related table field name "%s" from given relation key "%s"`,
|
||||||
relationResultFieldName,
|
relationResultFieldName,
|
||||||
@ -147,7 +147,8 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
|||||||
return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`)
|
return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`)
|
||||||
}
|
}
|
||||||
if relationResultFieldName != "" {
|
if relationResultFieldName != "" {
|
||||||
relationDataMap = r.MapKeyValue(relationResultFieldName)
|
// Note that the value might be type of slice.
|
||||||
|
relationDataMap = result.MapKeyValue(relationResultFieldName)
|
||||||
}
|
}
|
||||||
if len(relationDataMap) == 0 {
|
if len(relationDataMap) == 0 {
|
||||||
return gerror.Newf(`cannot find the relation data map, maybe invalid relation given "%v"`, relationKV)
|
return gerror.Newf(`cannot find the relation data map, maybe invalid relation given "%v"`, relationKV)
|
||||||
@ -239,12 +240,19 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
|||||||
if len(relationDataMap) > 0 {
|
if len(relationDataMap) > 0 {
|
||||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
|
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
|
||||||
if relationFromAttrField.IsValid() {
|
if relationFromAttrField.IsValid() {
|
||||||
if err = gconv.Structs(
|
results := make(Result, 0)
|
||||||
relationDataMap[gconv.String(relationFromAttrField.Interface())],
|
for _, v := range relationDataMap[gconv.String(relationFromAttrField.Interface())].Slice() {
|
||||||
bindToAttrValue.Addr(),
|
results = append(results, v.(Record))
|
||||||
); err != nil {
|
}
|
||||||
|
if err = results.Structs(bindToAttrValue.Addr()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Recursively Scan.
|
||||||
|
if model != nil {
|
||||||
|
if err = model.doWithScanStructs(bindToAttrValue.Addr()); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// May be the attribute does not exist yet.
|
// May be the attribute does not exist yet.
|
||||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||||
@ -268,24 +276,36 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
|||||||
// There's no relational data.
|
// There's no relational data.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err = gconv.Struct(v, element); err != nil {
|
if v.IsSlice() {
|
||||||
return err
|
if err = v.Slice()[0].(Record).Struct(element); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err = v.Val().(Record).Struct(element); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// May be the attribute does not exist yet.
|
// May be the attribute does not exist yet.
|
||||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if i >= len(r) {
|
if i >= len(result) {
|
||||||
// There's no relational data.
|
// There's no relational data.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v := r[i]
|
v := result[i]
|
||||||
if v == nil {
|
if v == nil {
|
||||||
// There's no relational data.
|
// There's no relational data.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err = gconv.Struct(v, element); err != nil {
|
if err = v.Struct(element); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Recursively Scan.
|
||||||
|
if model != nil {
|
||||||
|
if err = model.doWithScanStruct(element); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -300,24 +320,36 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
|||||||
// There's no relational data.
|
// There's no relational data.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
|
if relationDataItem.IsSlice() {
|
||||||
return err
|
if err = relationDataItem.Slice()[0].(Record).Struct(bindToAttrValue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err = relationDataItem.Val().(Record).Struct(bindToAttrValue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// May be the attribute does not exist yet.
|
// May be the attribute does not exist yet.
|
||||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if i >= len(r) {
|
if i >= len(result) {
|
||||||
// There's no relational data.
|
// There's no relational data.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
relationDataItem := r[i]
|
relationDataItem := result[i]
|
||||||
if relationDataItem == nil {
|
if relationDataItem == nil {
|
||||||
// There's no relational data.
|
// There's no relational data.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
|
if err = relationDataItem.Struct(bindToAttrValue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Recursively Scan.
|
||||||
|
if model != nil {
|
||||||
|
if err = model.doWithScanStruct(bindToAttrValue); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,11 @@ package gdb_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/gogf/gf/debug/gdebug"
|
||||||
"github.com/gogf/gf/frame/g"
|
"github.com/gogf/gf/frame/g"
|
||||||
|
"github.com/gogf/gf/os/gfile"
|
||||||
"github.com/gogf/gf/test/gtest"
|
"github.com/gogf/gf/test/gtest"
|
||||||
|
"github.com/gogf/gf/text/gstr"
|
||||||
"github.com/gogf/gf/util/gmeta"
|
"github.com/gogf/gf/util/gmeta"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -1189,3 +1192,161 @@ PRIMARY KEY (id)
|
|||||||
t.Assert(user.UserDetail.UserScores[4].Score, 5)
|
t.Assert(user.UserDetail.UserScores[4].Score, 5)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_Table_Relation_With_MultipleDepends1(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
dropTable("table_a")
|
||||||
|
dropTable("table_b")
|
||||||
|
dropTable("table_c")
|
||||||
|
}()
|
||||||
|
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||||
|
if _, err := db.Exec(v); err != nil {
|
||||||
|
gtest.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableC struct {
|
||||||
|
gmeta.Meta `orm:"table_c"`
|
||||||
|
Id int `orm:"id,primary" json:"id"`
|
||||||
|
TableBId int `orm:"table_b_id" json:"table_b_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableB struct {
|
||||||
|
gmeta.Meta `orm:"table_b"`
|
||||||
|
Id int `orm:"id,primary" json:"id"`
|
||||||
|
TableAId int `orm:"table_a_id" json:"table_a_id"`
|
||||||
|
TableC *TableC `orm:"with:table_b_id=id" json:"table_c"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableA struct {
|
||||||
|
gmeta.Meta `orm:"table_a"`
|
||||||
|
Id int `orm:"id,primary" json:"id"`
|
||||||
|
TableB *TableB `orm:"with:table_a_id=id" json:"table_b"`
|
||||||
|
}
|
||||||
|
|
||||||
|
db.SetDebug(true)
|
||||||
|
defer db.SetDebug(false)
|
||||||
|
|
||||||
|
// Struct.
|
||||||
|
gtest.C(t, func(t *gtest.T) {
|
||||||
|
var tableA *TableA
|
||||||
|
err := db.Model("table_a").WithAll().Scan(&tableA)
|
||||||
|
//g.Dump(tableA)
|
||||||
|
t.AssertNil(err)
|
||||||
|
t.AssertNE(tableA, nil)
|
||||||
|
t.Assert(tableA.Id, 1)
|
||||||
|
|
||||||
|
t.AssertNE(tableA.TableB, nil)
|
||||||
|
t.AssertNE(tableA.TableB.TableC, nil)
|
||||||
|
t.Assert(tableA.TableB.TableAId, 1)
|
||||||
|
t.Assert(tableA.TableB.TableC.Id, 100)
|
||||||
|
t.Assert(tableA.TableB.TableC.TableBId, 10)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Structs
|
||||||
|
gtest.C(t, func(t *gtest.T) {
|
||||||
|
var tableA []*TableA
|
||||||
|
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
|
||||||
|
//g.Dump(tableA)
|
||||||
|
t.AssertNil(err)
|
||||||
|
t.Assert(len(tableA), 2)
|
||||||
|
t.AssertNE(tableA[0].TableB, nil)
|
||||||
|
t.AssertNE(tableA[1].TableB, nil)
|
||||||
|
t.AssertNE(tableA[0].TableB.TableC, nil)
|
||||||
|
t.AssertNE(tableA[1].TableB.TableC, nil)
|
||||||
|
|
||||||
|
t.Assert(tableA[0].Id, 1)
|
||||||
|
t.Assert(tableA[0].TableB.Id, 10)
|
||||||
|
t.Assert(tableA[0].TableB.TableC.Id, 100)
|
||||||
|
|
||||||
|
t.Assert(tableA[1].Id, 2)
|
||||||
|
t.Assert(tableA[1].TableB.Id, 20)
|
||||||
|
t.Assert(tableA[1].TableB.TableC.Id, 300)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
func Test_Table_Relation_With_MultipleDepends2(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
dropTable("table_a")
|
||||||
|
dropTable("table_b")
|
||||||
|
dropTable("table_c")
|
||||||
|
}()
|
||||||
|
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||||
|
if _, err := db.Exec(v); err != nil {
|
||||||
|
gtest.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableC struct {
|
||||||
|
gmeta.Meta `orm:"table_c"`
|
||||||
|
Id int `orm:"id,primary" json:"id"`
|
||||||
|
TableBId int `orm:"table_b_id" json:"table_b_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableB struct {
|
||||||
|
gmeta.Meta `orm:"table_b"`
|
||||||
|
Id int `orm:"id,primary" json:"id"`
|
||||||
|
TableAId int `orm:"table_a_id" json:"table_a_id"`
|
||||||
|
TableC []*TableC `orm:"with:table_b_id=id" json:"table_c"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableA struct {
|
||||||
|
gmeta.Meta `orm:"table_a"`
|
||||||
|
Id int `orm:"id,primary" json:"id"`
|
||||||
|
TableB []*TableB `orm:"with:table_a_id=id" json:"table_b"`
|
||||||
|
}
|
||||||
|
|
||||||
|
db.SetDebug(true)
|
||||||
|
defer db.SetDebug(false)
|
||||||
|
|
||||||
|
// Struct.
|
||||||
|
gtest.C(t, func(t *gtest.T) {
|
||||||
|
var tableA *TableA
|
||||||
|
err := db.Model("table_a").WithAll().Scan(&tableA)
|
||||||
|
//g.Dump(tableA)
|
||||||
|
t.AssertNil(err)
|
||||||
|
t.AssertNE(tableA, nil)
|
||||||
|
t.Assert(tableA.Id, 1)
|
||||||
|
|
||||||
|
t.Assert(len(tableA.TableB), 2)
|
||||||
|
t.Assert(tableA.TableB[0].Id, 10)
|
||||||
|
t.Assert(tableA.TableB[1].Id, 30)
|
||||||
|
|
||||||
|
t.Assert(len(tableA.TableB[0].TableC), 2)
|
||||||
|
t.Assert(len(tableA.TableB[1].TableC), 1)
|
||||||
|
t.Assert(tableA.TableB[0].TableC[0].Id, 100)
|
||||||
|
t.Assert(tableA.TableB[0].TableC[0].TableBId, 10)
|
||||||
|
t.Assert(tableA.TableB[0].TableC[1].Id, 200)
|
||||||
|
t.Assert(tableA.TableB[0].TableC[1].TableBId, 10)
|
||||||
|
t.Assert(tableA.TableB[1].TableC[0].Id, 400)
|
||||||
|
t.Assert(tableA.TableB[1].TableC[0].TableBId, 30)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Structs
|
||||||
|
gtest.C(t, func(t *gtest.T) {
|
||||||
|
var tableA []*TableA
|
||||||
|
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
|
||||||
|
//g.Dump(tableA)
|
||||||
|
t.AssertNil(err)
|
||||||
|
t.Assert(len(tableA), 2)
|
||||||
|
|
||||||
|
t.Assert(len(tableA[0].TableB), 2)
|
||||||
|
t.Assert(tableA[0].TableB[0].Id, 10)
|
||||||
|
t.Assert(tableA[0].TableB[1].Id, 30)
|
||||||
|
|
||||||
|
t.Assert(len(tableA[0].TableB[0].TableC), 2)
|
||||||
|
t.Assert(len(tableA[0].TableB[1].TableC), 1)
|
||||||
|
t.Assert(tableA[0].TableB[0].TableC[0].Id, 100)
|
||||||
|
t.Assert(tableA[0].TableB[0].TableC[0].TableBId, 10)
|
||||||
|
t.Assert(tableA[0].TableB[0].TableC[1].Id, 200)
|
||||||
|
t.Assert(tableA[0].TableB[0].TableC[1].TableBId, 10)
|
||||||
|
t.Assert(tableA[0].TableB[1].TableC[0].Id, 400)
|
||||||
|
t.Assert(tableA[0].TableB[1].TableC[0].TableBId, 30)
|
||||||
|
|
||||||
|
t.Assert(tableA[1].TableB[0].TableC[0].Id, 300)
|
||||||
|
t.Assert(tableA[1].TableB[0].TableC[0].TableBId, 20)
|
||||||
|
|
||||||
|
t.Assert(tableA[1].TableB[1].Id, 40)
|
||||||
|
t.Assert(tableA[1].TableB[1].TableAId, 2)
|
||||||
|
t.Assert(tableA[1].TableB[1].TableC, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
33
database/gdb/testdata/with_multiple_depends.sql
vendored
Normal file
33
database/gdb/testdata/with_multiple_depends.sql
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
CREATE TABLE `table_a` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||||
|
|
||||||
|
INSERT INTO `table_a` VALUES (1, 'table_a_test1');
|
||||||
|
INSERT INTO `table_a` VALUES (2, 'table_a_test2');
|
||||||
|
|
||||||
|
CREATE TABLE `table_b` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`table_a_id` int(11) NOT NULL,
|
||||||
|
`alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||||
|
|
||||||
|
INSERT INTO `table_b` VALUES (10, 1, 'table_b_test1');
|
||||||
|
INSERT INTO `table_b` VALUES (20, 2, 'table_b_test2');
|
||||||
|
INSERT INTO `table_b` VALUES (30, 1, 'table_b_test3');
|
||||||
|
INSERT INTO `table_b` VALUES (40, 2, 'table_b_test4');
|
||||||
|
|
||||||
|
CREATE TABLE `table_c` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`table_b_id` int(11) NOT NULL,
|
||||||
|
`alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
|
||||||
|
|
||||||
|
INSERT INTO `table_c` VALUES (100, 10, 'table_c_test1');
|
||||||
|
INSERT INTO `table_c` VALUES (200, 10, 'table_c_test2');
|
||||||
|
INSERT INTO `table_c` VALUES (300, 20, 'table_c_test3');
|
||||||
|
INSERT INTO `table_c` VALUES (400, 30, 'table_c_test4');
|
Loading…
Reference in New Issue
Block a user